Skip to main content

Testing Strategy & Setup

Ever Works uses a dual testing framework approach: Jest for the core agent package and API, and Vitest for plugin packages. This division reflects the different build tooling -- the agent and API use NestJS with SWC (Jest-compatible), while plugins are standalone ESM packages built with tsup (Vitest-compatible).

Test Suite Overview​

PackageFrameworkSuitesTestsConfig
@ever-works/agentJest + ts-jest~26~719packages/agent/jest.config.js
apps/apiJestvariesvariesapps/api/jest.config.js
Each plugin (packages/plugins/*)Vitest1-3 per pluginvariesvitest.config.ts per plugin
@ever-works/pluginVitestvariesvariespackages/plugin/vitest.config.ts

Running Tests​

All Tests​

pnpm test # Run all tests across the monorepo

Agent Package (Jest)​

cd packages/agent

pnpm test # All agent tests
pnpm test:watch # Watch mode for TDD
pnpm test:cov # Run with coverage reporting

# Single test file or pattern
npx jest --testPathPattern='generators'
npx jest --testPathPattern='plugin-registry'

Plugin Packages (Vitest)​

cd packages/plugins/openai

pnpm test # All tests for a single plugin
npx vitest run src/openai.spec.ts # Single file

API Tests (Jest)​

cd apps/api
pnpm test

Jest Configuration (Agent)​

The agent Jest config in packages/agent/jest.config.js has several important settings:

module.exports = {
moduleFileExtensions: ['js', 'json', 'ts'],
rootDir: 'src',
testRegex: '.*\\.spec\\.ts$',
transform: {
'^.+\\.(t|j)s$': [
'ts-jest',
{
diagnostics: { ignoreCodes: [151002] }
}
]
},
testEnvironment: 'node',
moduleNameMapper: {
'^@src/(.*)$': '<rootDir>/$1',
'^@ever-works/plugin$': '<rootDir>/../../plugin/src/index.ts',
'^@ever-works/plugin/(.*)$': '<rootDir>/../../plugin/src/$1/index.ts',
'^@ever-works/contracts$': '<rootDir>/../../contracts/src/index.ts',
'^@ever-works/contracts/(.*)$': '<rootDir>/../../contracts/src/$1/index.ts',
'^(\\.{1,2}/.*)\\.js$': '$1'
}
};

Key points:

  • moduleNameMapper resolves workspace packages (@ever-works/plugin, @ever-works/contracts) directly to their TypeScript source files. This avoids needing to build those packages before running tests.
  • The .js extension mapper handles ESM-style imports that reference .js but resolve to .ts source.
  • ts-jest is used for TypeScript transformation with diagnostic code 151002 suppressed (related to isolated module warnings).
  • Test files must match the *.spec.ts pattern.

Vitest Configuration (Plugins)​

Each plugin has a vitest.config.ts:

import { defineConfig } from 'vitest/config';

export default defineConfig({
test: {
environment: 'node',
globals: true,
include: ['src/**/*.{test,spec}.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html']
}
}
});

Vitest provides faster execution for the smaller, self-contained plugin packages and supports ESM natively.

Test Philosophy​

The testing approach prioritizes:

  1. Unit tests for business logic -- Services, selectors, and utility functions are tested in isolation with mocked dependencies.
  2. NestJS Testing Module -- Services that rely on dependency injection use @nestjs/testing to create properly wired test modules.
  3. Mocked external services -- Git operations, AI calls, and database queries are mocked at the facade/repository boundary.
  4. Pure function tests -- Algorithms like pair selection, slug generation, and data transformation are tested as pure functions without any framework scaffolding.

Build Before Test​

Some packages require workspace dependencies to be built before tests can run. If you encounter resolution errors:

pnpm build # Build all packages from root
pnpm test # Then run tests

The moduleNameMapper in Jest resolves most workspace imports to source, but transitive dependencies may still require builds.

Coverage​

Generate coverage reports for the agent package:

cd packages/agent
pnpm test:cov

Coverage output goes to packages/agent/coverage/ and includes:

  • text -- console summary
  • json -- machine-readable data
  • html -- browsable HTML report

Coverage collection is configured to include all .ts and .js files under src/.