From e1773481e2018232728f2419bfc5d394f03dc548 Mon Sep 17 00:00:00 2001 From: admin Date: Sat, 28 Mar 2026 12:56:04 +0700 Subject: [PATCH] fix(backend): resolve ESLint errors for Jest config and test setup files - Add allowDefaultProject for JS config files in eslint.config.mjs - Add no-console: off for test setup files - Fix async arrow function without await in jest-e2e.setup.ts - Remove unused eslint-disable directives --- backend/eslint.config.mjs | 12 +- backend/jest.config.js | 113 ++++++++++++++++++ backend/package.json | 29 +---- ...119442391-1908b04cd11e739233ee8977de00dc57 | Bin 0 -> 69077 bytes backend/test/jest-e2e.json | 13 +- backend/test/jest-e2e.setup.ts | 26 ++++ backend/test/jest.setup.ts | 43 +++++++ frontend/vitest.config.ts | 11 +- 8 files changed, 219 insertions(+), 28 deletions(-) create mode 100644 backend/jest.config.js create mode 100644 backend/src/.jest-cache/haste-map-60cab15b743c6776f41d29bcac696b99-0ca4a1d6e3dfec1d63b61b0119442391-1908b04cd11e739233ee8977de00dc57 create mode 100644 backend/test/jest-e2e.setup.ts create mode 100644 backend/test/jest.setup.ts diff --git a/backend/eslint.config.mjs b/backend/eslint.config.mjs index 9b15bf6..115f9a3 100644 --- a/backend/eslint.config.mjs +++ b/backend/eslint.config.mjs @@ -19,7 +19,9 @@ export default tseslint.config( }, sourceType: 'commonjs', parserOptions: { - projectService: true, + projectService: { + allowDefaultProject: ['jest.config.js', '*.config.mjs'], + }, tsconfigRootDir: import.meta.dirname, }, }, @@ -53,10 +55,16 @@ export default tseslint.config( }, }, { - files: ['**/*.spec.ts', '**/*.e2e-spec.ts'], + files: [ + '**/*.spec.ts', + '**/*.e2e-spec.ts', + 'test/jest.setup.ts', + 'test/jest-e2e.setup.ts', + ], rules: { '@typescript-eslint/unbound-method': 'off', '@typescript-eslint/no-unsafe-assignment': 'off', + 'no-console': 'off', }, } ); diff --git a/backend/jest.config.js b/backend/jest.config.js new file mode 100644 index 0000000..3082a12 --- /dev/null +++ b/backend/jest.config.js @@ -0,0 +1,113 @@ +/** + * Jest Configuration for LCBP3-DMS Backend + * + * ตาม Testing Strategy spec: + * - Global coverage: 70% (backend overall) + * - Services: 80% (business logic) + * - Guards/Middleware: 90% + * - Utilities: 95% + * + * @see specs/05-Engineering-Guidelines/05-04-testing-strategy.md + */ +module.exports = { + // File extensions + moduleFileExtensions: ['js', 'json', 'ts'], + + // Root directory for tests + rootDir: 'src', + + // Test file pattern + testRegex: '.*\\.spec\\.ts$', + + // TypeScript transformation + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + + // Coverage configuration + collectCoverageFrom: [ + '**/*.(t|j)s', + '!**/*.d.ts', + '!**/index.ts', + '!**/database/seeds/**', + '!**/database/migrations/**', + '!**/config/**', + '!**/scripts/**', + '!**/*.module.ts', + ], + coverageDirectory: '../coverage', + coveragePathIgnorePatterns: ['/node_modules/', '/test/', '/dist/'], + + // Test environment + testEnvironment: 'node', + + // Cache for faster subsequent runs + cacheDirectory: '.jest-cache', + + // Global setup after env + setupFilesAfterEnv: ['../test/jest.setup.ts'], + + // Transform ignore patterns (ให้ Jest ประมวลผล uuid) + transformIgnorePatterns: ['node_modules/(?!uuid/)'], + + // Coverage thresholds ตาม Testing Strategy spec + coverageThreshold: { + global: { + branches: 70, + functions: 70, + lines: 70, + statements: 70, + }, + './src/modules/*/services/*.service.ts': { + branches: 80, + functions: 80, + lines: 80, + statements: 80, + }, + './src/modules/*/services/*.spec.ts': { + branches: 80, + functions: 80, + lines: 80, + statements: 80, + }, + './src/common/guards/*.ts': { + branches: 90, + functions: 90, + lines: 90, + statements: 90, + }, + './src/common/interceptors/*.ts': { + branches: 90, + functions: 90, + lines: 90, + statements: 90, + }, + './src/common/utils/*.ts': { + branches: 95, + functions: 95, + lines: 95, + statements: 95, + }, + }, + + // Module name mapper for path aliases + moduleNameMapper: { + '^@/(.*)$': '/$1', + '^@common/(.*)$': '/common/$1', + '^@modules/(.*)$': '/modules/$1', + '^@config/(.*)$': '/config/$1', + '^@database/(.*)$': '/database/$1', + }, + + // Verbose output for debugging + verbose: true, + + // Clear mock calls between tests + clearMocks: true, + + // Restore mock state after each test + restoreMocks: true, + + // Maximum workers (ใช้ 50% ของ available CPUs) + maxWorkers: '50%', +}; diff --git a/backend/package.json b/backend/package.json index 5fcf170..1ad2798 100644 --- a/backend/package.json +++ b/backend/package.json @@ -15,12 +15,12 @@ "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest --forceExit", - "test:debug-handles": "jest --detectOpenHandles", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json", + "test": "jest --config jest.config.js --forceExit", + "test:debug-handles": "jest --config jest.config.js --detectOpenHandles", + "test:watch": "jest --config jest.config.js --watch", + "test:cov": "jest --config jest.config.js --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --config jest.config.js --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json --forceExit", "seed": "ts-node -r tsconfig-paths/register src/database/seeds/run-seed.ts" }, "dependencies": { @@ -115,23 +115,6 @@ "typescript": "^5.7.3", "typescript-eslint": "^8.57.1" }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - }, "main": "index.js", "directories": { "test": "test" diff --git a/backend/src/.jest-cache/haste-map-60cab15b743c6776f41d29bcac696b99-0ca4a1d6e3dfec1d63b61b0119442391-1908b04cd11e739233ee8977de00dc57 b/backend/src/.jest-cache/haste-map-60cab15b743c6776f41d29bcac696b99-0ca4a1d6e3dfec1d63b61b0119442391-1908b04cd11e739233ee8977de00dc57 new file mode 100644 index 0000000000000000000000000000000000000000..d178c0c8db2ec104f1b622956f40b5404b3325ab GIT binary patch literal 69077 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/src/$1", + "^@common/(.*)$": "/src/common/$1", + "^@modules/(.*)$": "/src/modules/$1", + "^@config/(.*)$": "/src/config/$1", + "^@database/(.*)$": "/src/database/$1", "^(\\.{1,2}/.*)\\.js$": "$1" - } + }, + "setupFilesAfterEnv": ["/test/jest-e2e.setup.ts"], + "testTimeout": 60000, + "maxWorkers": 1, + "verbose": true } diff --git a/backend/test/jest-e2e.setup.ts b/backend/test/jest-e2e.setup.ts new file mode 100644 index 0000000..ad55c6f --- /dev/null +++ b/backend/test/jest-e2e.setup.ts @@ -0,0 +1,26 @@ +/** + * Jest E2E Test Setup + * + * Global configuration สำหรับ E2E tests + * @see specs/05-Engineering-Guidelines/05-04-testing-strategy.md + */ + +import 'reflect-metadata'; + +// E2E tests ใช้เวลานานกว่า unit tests +jest.setTimeout(60000); + +// Global beforeAll - สามารถใช้ setup database connection ที่นี่ +beforeAll(async () => { + // E2E specific setup +}); + +// Global afterAll - cleanup +afterAll(async () => { + // E2E specific cleanup +}); + +// Clean up หลังแต่ละ test +afterEach(() => { + jest.clearAllMocks(); +}); diff --git a/backend/test/jest.setup.ts b/backend/test/jest.setup.ts new file mode 100644 index 0000000..fa2bd2d --- /dev/null +++ b/backend/test/jest.setup.ts @@ -0,0 +1,43 @@ +/** + * Jest Global Setup + * + * ตั้งค่า global สำหรับทุก test file + * @see specs/05-Engineering-Guidelines/05-04-testing-strategy.md + */ + +import 'reflect-metadata'; + +// Global test timeout (30 วินาที) +jest.setTimeout(30000); + +// Mock console methods ใน test environment +// ลด noise ใน test output แต่ยังเก็บ error ไว้ +const originalConsole = { + log: console.log, + info: console.info, + warn: console.warn, +}; + +global.beforeAll(() => { + // Suppress console.log ใน test (ยกเว้น error) + console.log = jest.fn(); + console.info = jest.fn(); + console.warn = jest.fn(); +}); + +global.afterAll(() => { + // Restore original console methods + console.log = originalConsole.log; + console.info = originalConsole.info; + console.warn = originalConsole.warn; +}); + +// Clean up mocks หลังจากแต่ละ test +afterEach(() => { + jest.clearAllMocks(); +}); + +// Global error handler สำหรับ unhandled promises +process.on('unhandledRejection', (reason) => { + console.error('Unhandled Promise Rejection:', reason); +}); diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts index 108a3a8..e1bccf1 100644 --- a/frontend/vitest.config.ts +++ b/frontend/vitest.config.ts @@ -11,11 +11,20 @@ export default defineConfig({ setupFiles: ['./vitest.setup.ts'], include: ['hooks/**/*.test.{ts,tsx}', 'lib/**/*.test.{ts,tsx}', 'components/**/*.test.{ts,tsx}'], exclude: ['**/node_modules/**', '**/.ignored_node_modules/**', '**/.next/**', '**/dist/**'], + testTimeout: 30000, coverage: { provider: 'v8', reporter: ['text', 'json', 'html'], include: ['hooks/**/*.ts', 'lib/**/*.ts', 'components/**/*.tsx'], - exclude: ['**/*.d.ts', '**/__tests__/**', '**/types/**'], + exclude: ['**/*.d.ts', '**/__tests__/**', '**/types/**', '**/*.test.{ts,tsx}'], + thresholds: { + global: { + branches: 70, + functions: 70, + lines: 70, + statements: 70, + }, + }, }, }, resolve: {