first implementation

This commit is contained in:
Wuesteon 2025-11-27 17:26:18 +01:00
parent 98efa6f6e8
commit 74dc6892ab
61 changed files with 30899 additions and 4934 deletions

View file

@ -0,0 +1,172 @@
# @manacore/test-config
Shared test configurations for all projects in the Manacore monorepo.
## Available Configurations
### Jest Configuration for NestJS Backends
```javascript
// jest.config.js
const baseConfig = require('@manacore/test-config/jest-backend');
module.exports = {
...baseConfig,
// Your project-specific overrides
};
```
### Jest Configuration for React Native Mobile
```javascript
// jest.config.js
module.exports = {
preset: '@manacore/test-config/jest-mobile',
// Your project-specific overrides
};
```
### Vitest Configuration for Shared Packages
```typescript
// vitest.config.ts
import { defineConfig, mergeConfig } from 'vitest/config';
import baseConfig from '@manacore/test-config/vitest-base';
export default mergeConfig(
baseConfig,
defineConfig({
// Your project-specific overrides
})
);
```
### Vitest Configuration for SvelteKit Web Apps
```typescript
// vitest.config.ts
import { defineConfig, mergeConfig } from 'vitest/config';
import svelteConfig from '@manacore/test-config/vitest-svelte';
import { sveltekit } from '@sveltejs/kit/vite';
export default mergeConfig(
svelteConfig,
defineConfig({
plugins: [sveltekit()],
// Your project-specific overrides
})
);
```
### Playwright Configuration for E2E Tests
```typescript
// playwright.config.ts
import { defineConfig } from '@playwright/test';
import baseConfig from '@manacore/test-config/playwright';
export default defineConfig({
...baseConfig,
use: {
...baseConfig.use,
baseURL: 'http://localhost:5173',
},
// Your project-specific overrides
});
```
## Features
### Common Settings Across All Configs
- **Coverage Thresholds**: 80% for lines, functions, branches, statements
- **Mock Management**: Auto-clear, restore, and reset mocks between tests
- **Timeout**: 10s default for tests
- **Verbose Output**: In CI environments
- **Error Handling**: Fail on deprecated APIs
### NestJS Backend Config
- TypeScript support via ts-jest
- Automatic exclusion of modules, DTOs, entities
- Module path aliases support
- Coverage collection from source files
### React Native Mobile Config
- jest-expo preset
- Transform ignore patterns for React Native modules
- Support for @manacore packages
- Coverage from src/ and app/ directories
### Vitest Configs
- Modern, fast test runner
- Coverage via v8
- ESM support
- Global test APIs (describe, it, expect)
### Playwright Config
- Multi-browser testing (Chromium, Firefox, WebKit)
- Mobile viewport testing
- Built-in retry logic
- Video/screenshot on failure
- Auto-start web server
## Adding to Your Project
1. **Install peer dependencies**:
```bash
# For NestJS backend
pnpm add -D jest ts-jest @types/jest
# For React Native mobile
pnpm add -D jest jest-expo @testing-library/react-native
# For SvelteKit web
pnpm add -D vitest @vitest/coverage-v8 jsdom
# For E2E tests
pnpm add -D @playwright/test
```
2. **Create config file** in your project root (see examples above)
3. **Add test scripts** to package.json:
```json
{
"scripts": {
"test": "jest", // or "vitest run"
"test:watch": "jest --watch", // or "vitest"
"test:cov": "jest --coverage", // or "vitest run --coverage"
"test:e2e": "playwright test"
}
}
```
## Customization
Each config can be extended with project-specific settings:
```typescript
// Override coverage thresholds
export default mergeConfig(baseConfig, {
test: {
coverage: {
thresholds: {
lines: 90, // More strict for critical packages
},
},
},
});
```
## Related Documentation
- [Testing Strategy](../../docs/TESTING.md)
- [Jest Documentation](https://jestjs.io/)
- [Vitest Documentation](https://vitest.dev/)
- [Playwright Documentation](https://playwright.dev/)

View file

@ -0,0 +1,94 @@
/**
* Shared Jest configuration for NestJS backend projects
*
* Usage in backend package.json:
* {
* "jest": {
* "preset": "@manacore/test-config/jest-backend"
* }
* }
*
* Or extend in jest.config.js:
* const baseConfig = require('@manacore/test-config/jest-backend');
* module.exports = {
* ...baseConfig,
* // Your overrides
* };
*/
module.exports = {
// File extensions Jest should look for
moduleFileExtensions: ['js', 'json', 'ts'],
// Root directory for tests (relative to project root)
rootDir: 'src',
// Test file pattern
testRegex: '.*\\.spec\\.ts$',
// Transform TypeScript files
transform: {
'^.+\\.(t|j)s$': 'ts-jest',
},
// Collect coverage from these files
collectCoverageFrom: [
'**/*.(t|j)s',
'!**/*.module.ts', // Exclude NestJS modules
'!**/*.interface.ts', // Exclude interfaces
'!**/*.dto.ts', // Exclude DTOs
'!**/*.entity.ts', // Exclude entities
'!**/main.ts', // Exclude entry point
'!**/*.d.ts', // Exclude type definitions
'!**/node_modules/**',
'!**/__tests__/**', // Exclude test files
'!**/test/**',
],
// Coverage output directory
coverageDirectory: '../coverage',
// Test environment
testEnvironment: 'node',
// Coverage thresholds (fail if below these values)
coverageThresholds: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
// Module name mapper for path aliases
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/$1',
'^@core/(.*)$': '<rootDir>/core/$1',
'^@modules/(.*)$': '<rootDir>/modules/$1',
},
// Setup files
setupFilesAfterEnv: ['<rootDir>/../test/setup.ts'],
// Maximum time for tests
testTimeout: 10000,
// Clear mocks between tests
clearMocks: true,
// Restore mocks between tests
restoreMocks: true,
// Reset mocks between tests
resetMocks: true,
// Verbose output
verbose: true,
// Error on deprecated APIs
errorOnDeprecated: true,
// Paths to ignore
testPathIgnorePatterns: ['/node_modules/', '/dist/', '/__tests__/utils/', '/__tests__/fixtures/'],
};

View file

@ -0,0 +1,104 @@
/**
* Shared Jest configuration for React Native (Expo) mobile projects
*
* Usage in mobile package.json:
* {
* "jest": {
* "preset": "@manacore/test-config/jest-mobile"
* }
* }
*/
module.exports = {
// Use jest-expo preset
preset: 'jest-expo',
// Setup files to run after environment is set up
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
// Test file patterns
testMatch: ['**/__tests__/**/*.test.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'],
// Paths to ignore
testPathIgnorePatterns: ['/node_modules/', '/__tests__/utils/', '/__tests__/fixtures/', '/__tests__/mocks/'],
// Transform ignore patterns for React Native modules
transformIgnorePatterns: [
'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg|@manacore/.*)',
],
// Collect coverage from these files
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'app/**/*.{ts,tsx}',
'!**/*.d.ts',
'!**/node_modules/**',
'!**/__tests__/**',
'!**/coverage/**',
'!**/*.styles.ts', // Exclude style files
'!**/*.types.ts', // Exclude type-only files
],
// Coverage directory
coverageDirectory: 'coverage',
// Coverage thresholds
coverageThresholds: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
// Module name mapper
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'^@components/(.*)$': '<rootDir>/src/components/$1',
'^@services/(.*)$': '<rootDir>/src/services/$1',
'^@utils/(.*)$': '<rootDir>/src/utils/$1',
'^@hooks/(.*)$': '<rootDir>/src/hooks/$1',
'^@stores/(.*)$': '<rootDir>/src/stores/$1',
'^@assets/(.*)$': '<rootDir>/assets/$1',
},
// Test environment
testEnvironment: 'node',
// Maximum time for tests
testTimeout: 10000,
// Clear mocks between tests
clearMocks: true,
// Restore mocks between tests
restoreMocks: true,
// Reset mocks between tests
resetMocks: true,
// Verbose output in CI
verbose: process.env.CI === 'true',
// Coverage reporters
coverageReporters: ['text', 'lcov', 'html', 'json'],
// Error on deprecated APIs
errorOnDeprecated: true,
// Detect open handles
detectOpenHandles: true,
// Force exit after tests complete
forceExit: false,
// Globals
globals: {
'ts-jest': {
tsconfig: {
jsx: 'react',
},
},
},
};

View file

@ -0,0 +1,48 @@
{
"name": "@manacore/test-config",
"version": "0.1.0",
"description": "Shared test configurations for Manacore monorepo",
"private": true,
"type": "module",
"exports": {
"./jest-backend": "./jest.config.backend.js",
"./jest-mobile": "./jest.config.mobile.js",
"./vitest-base": "./vitest.config.base.ts",
"./vitest-svelte": "./vitest.config.svelte.ts",
"./playwright": "./playwright.config.base.ts"
},
"files": [
"*.js",
"*.ts"
],
"scripts": {
"type-check": "tsc --noEmit"
},
"devDependencies": {
"@types/node": "^24.10.1",
"typescript": "^5.9.3"
},
"peerDependencies": {
"@playwright/test": "^1.40.0",
"jest": "^29.0.0",
"vitest": "^3.0.0"
},
"peerDependenciesMeta": {
"@playwright/test": {
"optional": true
},
"jest": {
"optional": true
},
"vitest": {
"optional": true
}
},
"keywords": [
"testing",
"jest",
"vitest",
"playwright",
"config"
]
}

View file

@ -0,0 +1,107 @@
/**
* Base Playwright configuration for E2E tests
*
* Usage in project playwright.config.ts:
* import { defineConfig, devices } from '@playwright/test';
* import baseConfig from '@manacore/test-config/playwright';
*
* export default defineConfig({
* ...baseConfig,
* use: {
* ...baseConfig.use,
* baseURL: 'http://localhost:5173',
* },
* // Your overrides
* });
*/
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
// Test directory
testDir: './e2e',
// Run tests in parallel
fullyParallel: true,
// Fail build on CI if you accidentally left test.only
forbidOnly: !!process.env.CI,
// Retry on CI
retries: process.env.CI ? 2 : 0,
// Number of workers
workers: process.env.CI ? 1 : undefined,
// Reporter to use
reporter: process.env.CI ? [['github'], ['html', { open: 'never' }]] : [['html']],
// Shared settings for all projects
use: {
// Base URL for navigation
baseURL: 'http://localhost:5173',
// Collect trace on first retry
trace: 'on-first-retry',
// Screenshot on failure
screenshot: 'only-on-failure',
// Video on first retry
video: 'retain-on-failure',
// Timeout for actions
actionTimeout: 10000,
// Navigation timeout
navigationTimeout: 30000,
},
// Test timeout
timeout: 60000,
// Expect timeout
expect: {
timeout: 5000,
},
// Projects to run tests on
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
// Mobile viewports
{
name: 'Mobile Chrome',
use: { ...devices['Pixel 5'] },
},
{
name: 'Mobile Safari',
use: { ...devices['iPhone 12'] },
},
],
// Web server to start before tests
webServer: {
command: 'pnpm run build && pnpm run preview',
port: 5173,
reuseExistingServer: !process.env.CI,
timeout: 120000,
},
// Output directory for test results
outputDir: 'test-results/',
// Global setup/teardown
// globalSetup: require.resolve('./e2e/global-setup.ts'),
// globalTeardown: require.resolve('./e2e/global-teardown.ts'),
});

View file

@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"moduleResolution": "bundler",
"resolveJsonModule": true,
"allowJs": true,
"checkJs": false,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": ".",
"types": ["node"]
},
"include": ["**/*.ts", "**/*.js"],
"exclude": ["node_modules", "dist"]
}

View file

@ -0,0 +1,93 @@
/**
* Base Vitest configuration for shared packages
*
* Usage in package vitest.config.ts:
* import { defineConfig, mergeConfig } from 'vitest/config';
* import baseConfig from '@manacore/test-config/vitest-base';
*
* export default mergeConfig(
* baseConfig,
* defineConfig({
* // Your overrides
* })
* );
*/
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
// Test file patterns
include: ['src/**/*.{test,spec}.{js,ts}'],
// Exclude patterns
exclude: ['node_modules/**', 'dist/**', '**/*.d.ts', '**/__tests__/fixtures/**', '**/__tests__/utils/**'],
// Test environment
environment: 'node',
// Global test APIs (describe, it, expect, etc.)
globals: true,
// Setup files
setupFiles: ['./vitest.setup.ts'],
// Coverage configuration
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html', 'lcov'],
include: ['src/**/*.{js,ts}'],
exclude: [
'**/*.d.ts',
'**/*.config.*',
'**/__tests__/**',
'**/node_modules/**',
'**/dist/**',
'**/coverage/**',
'**/*.types.ts',
'**/index.ts', // Usually just re-exports
],
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80,
},
all: true,
},
// Test timeout
testTimeout: 10000,
// Hooks timeout
hookTimeout: 10000,
// Teardown timeout
teardownTimeout: 10000,
// Watch mode ignore patterns
watchExclude: ['**/node_modules/**', '**/dist/**', '**/coverage/**'],
// Reporters
reporters: process.env.CI ? ['verbose', 'github-actions'] : ['verbose'],
// Mock reset
clearMocks: true,
mockReset: true,
restoreMocks: true,
// Fail on console errors
onConsoleLog: (log: string, type: 'stdout' | 'stderr'): false | void => {
if (type === 'stderr' && log.includes('Error')) {
return false; // Fail test on console errors
}
},
},
// Resolve aliases
resolve: {
alias: {
'@': '/src',
},
},
});

View file

@ -0,0 +1,95 @@
/**
* Vitest configuration for SvelteKit web projects
*
* Usage in web vitest.config.ts:
* import { defineConfig, mergeConfig } from 'vitest/config';
* import svelteConfig from '@manacore/test-config/vitest-svelte';
* import { sveltekit } from '@sveltejs/kit/vite';
*
* export default mergeConfig(
* svelteConfig,
* defineConfig({
* plugins: [sveltekit()],
* // Your overrides
* })
* );
*/
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
// Test file patterns
include: ['src/**/*.{test,spec}.{js,ts}'],
// Exclude patterns
exclude: ['node_modules/**', 'e2e/**', 'build/**', '.svelte-kit/**', '**/*.d.ts'],
// Test environment for browser APIs
environment: 'jsdom',
// Global test APIs
globals: true,
// Setup files
setupFiles: ['./vitest.setup.ts'],
// Coverage configuration
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html', 'lcov'],
include: ['src/**/*.{js,ts,svelte}'],
exclude: [
'**/*.d.ts',
'**/*.config.*',
'**/mockData/**',
'**/__tests__/**',
'**/node_modules/**',
'**/build/**',
'**/.svelte-kit/**',
'**/coverage/**',
'src/routes/**/+*.ts', // Exclude SvelteKit route files from coverage (tested via E2E)
'src/routes/**/+*.server.ts', // Test these explicitly
],
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80,
},
all: true,
},
// Test timeout
testTimeout: 10000,
// Hooks timeout
hookTimeout: 10000,
// Watch mode ignore patterns
watchExclude: ['**/node_modules/**', '**/build/**', '**/.svelte-kit/**', '**/coverage/**'],
// Reporters
reporters: process.env.CI ? ['verbose', 'github-actions'] : ['verbose'],
// Mock reset
clearMocks: true,
mockReset: true,
restoreMocks: true,
// Browser mode (optional - for testing Svelte components in real browser)
// browser: {
// enabled: false, // Enable when needed
// name: 'chromium',
// provider: 'playwright',
// },
},
// Resolve aliases (adjust based on your SvelteKit config)
resolve: {
alias: {
$lib: '/src/lib',
$app: '/.svelte-kit/runtime/app',
},
},
});