Set up comprehensive frontend testing infrastructure
- Install Jest for unit testing with React Testing Library - Install Playwright for end-to-end testing - Configure Jest with proper TypeScript support and module mapping - Create test setup files and utilities for both unit and e2e tests Components: * Jest configuration with coverage thresholds * Playwright configuration with browser automation * Unit tests for LoginForm, AuthContext, and useSocketIO hook * E2E tests for authentication, dashboard, and agents workflows * GitHub Actions workflow for automated testing * Mock data and API utilities for consistent testing * Test documentation with best practices Testing features: - Unit tests with 70% coverage threshold - E2E tests with API mocking and user journey testing - CI/CD integration for automated test runs - Cross-browser testing support with Playwright - Authentication system testing end-to-end 🚀 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							
								
								
									
										6
									
								
								frontend/node_modules/ts-jest/dist/cli/config/init.d.ts
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								frontend/node_modules/ts-jest/dist/cli/config/init.d.ts
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| /** | ||||
|  * This has been written quickly. While trying to improve I realised it'd be better to have it in Jest... | ||||
|  * ...and I saw a merged PR with `jest --init` tool! | ||||
|  * TODO: see what's the best path for this | ||||
|  */ | ||||
| export {}; | ||||
							
								
								
									
										138
									
								
								frontend/node_modules/ts-jest/dist/cli/config/init.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								frontend/node_modules/ts-jest/dist/cli/config/init.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | ||||
| "use strict"; | ||||
| /** | ||||
|  * This has been written quickly. While trying to improve I realised it'd be better to have it in Jest... | ||||
|  * ...and I saw a merged PR with `jest --init` tool! | ||||
|  * TODO: see what's the best path for this | ||||
|  */ | ||||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||||
|     return (mod && mod.__esModule) ? mod : { "default": mod }; | ||||
| }; | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| exports.help = exports.run = void 0; | ||||
| const fs_1 = require("fs"); | ||||
| const path_1 = require("path"); | ||||
| const ejs_1 = __importDefault(require("ejs")); | ||||
| const json5_1 = require("json5"); | ||||
| const create_jest_preset_1 = require("../../presets/create-jest-preset"); | ||||
| const JEST_CONFIG_EJS_TEMPLATE = `const { <%= presetCreatorFn %> } = require("ts-jest"); | ||||
|  | ||||
| const tsJestTransformCfg = <%= presetCreatorFn %>(<%- transformOpts %>).transform; | ||||
|  | ||||
| /** @type {import("jest").Config} **/ | ||||
| <%= exportKind %> { | ||||
|   testEnvironment: "<%= testEnvironment %>", | ||||
|   transform: { | ||||
|     ...tsJestTransformCfg, | ||||
|   }, | ||||
| };`; | ||||
| const ensureOnlyUsingDoubleQuotes = (str) => { | ||||
|     return str | ||||
|         .replace(/"'(.*?)'"/g, '"$1"') | ||||
|         .replace(/'ts-jest'/g, '"ts-jest"') | ||||
|         .replace(/'babel-jest'/g, '"babel-jest"'); | ||||
| }; | ||||
| /** | ||||
|  * @internal | ||||
|  */ | ||||
| const run = async (args /* , logger: Logger */) => { | ||||
|     const { tsconfig: askedTsconfig, force, jsdom, js: jsFilesProcessor, babel: shouldPostProcessWithBabel } = args; | ||||
|     const file = args._[0]?.toString() ?? 'jest.config.js'; | ||||
|     const filePath = (0, path_1.join)(process.cwd(), file); | ||||
|     const name = (0, path_1.basename)(file); | ||||
|     const isPackageJsonConfig = name === 'package.json'; | ||||
|     const isJestConfigFileExisted = (0, fs_1.existsSync)(filePath); | ||||
|     const pkgFile = isPackageJsonConfig ? filePath : (0, path_1.join)(process.cwd(), 'package.json'); | ||||
|     const isPackageJsonExisted = isPackageJsonConfig || (0, fs_1.existsSync)(pkgFile); | ||||
|     const tsconfig = askedTsconfig === 'tsconfig.json' ? undefined : askedTsconfig; | ||||
|     const pkgJsonContent = isPackageJsonExisted ? JSON.parse((0, fs_1.readFileSync)(pkgFile, 'utf8')) : {}; | ||||
|     if (shouldPostProcessWithBabel) { | ||||
|         console.warn(`The option --babel is deprecated and will be removed in the next major version.` + | ||||
|             ` Please specify 'js' option value (see more with npx ts-jest help) if you wish 'ts-jest' to process 'js' with TypeScript API or Babel.`); | ||||
|     } | ||||
|     if (isPackageJsonConfig && !isJestConfigFileExisted) { | ||||
|         throw new Error(`File ${file} does not exists.`); | ||||
|     } | ||||
|     else if (!isPackageJsonConfig && isJestConfigFileExisted && !force) { | ||||
|         throw new Error(`Configuration file ${file} already exists.`); | ||||
|     } | ||||
|     if (!isPackageJsonConfig && !name.endsWith('.js')) { | ||||
|         throw new TypeError(`Configuration file ${file} must be a .js file or the package.json.`); | ||||
|     } | ||||
|     if (isPackageJsonExisted && pkgJsonContent.jest) { | ||||
|         if (force && !isPackageJsonConfig) { | ||||
|             delete pkgJsonContent.jest; | ||||
|             (0, fs_1.writeFileSync)(pkgFile, JSON.stringify(pkgJsonContent, undefined, '  ')); | ||||
|         } | ||||
|         else if (!force) { | ||||
|             throw new Error(`A Jest configuration is already set in ${pkgFile}.`); | ||||
|         } | ||||
|     } | ||||
|     let body; | ||||
|     const transformOpts = tsconfig | ||||
|         ? { tsconfig: `${(0, json5_1.stringify)(tsconfig)}` } | ||||
|         : undefined; | ||||
|     let transformConfig; | ||||
|     if (isPackageJsonConfig) { | ||||
|         if (jsFilesProcessor === 'babel' || shouldPostProcessWithBabel) { | ||||
|             transformConfig = (0, create_jest_preset_1.createJsWithBabelPreset)(transformOpts); | ||||
|         } | ||||
|         else if (jsFilesProcessor === 'ts') { | ||||
|             transformConfig = (0, create_jest_preset_1.createJsWithTsPreset)(transformOpts); | ||||
|         } | ||||
|         else { | ||||
|             transformConfig = (0, create_jest_preset_1.createDefaultPreset)(transformOpts); | ||||
|         } | ||||
|         body = ensureOnlyUsingDoubleQuotes(JSON.stringify({ | ||||
|             ...pkgJsonContent, | ||||
|             jest: transformConfig, | ||||
|         }, undefined, '  ')); | ||||
|     } | ||||
|     else { | ||||
|         let presetCreatorFn; | ||||
|         if (jsFilesProcessor === 'babel' || shouldPostProcessWithBabel) { | ||||
|             presetCreatorFn = 'createJsWithBabelPreset'; | ||||
|         } | ||||
|         else if (jsFilesProcessor === 'ts') { | ||||
|             presetCreatorFn = 'createJsWithTsPreset'; | ||||
|         } | ||||
|         else { | ||||
|             presetCreatorFn = 'createDefaultPreset'; | ||||
|         } | ||||
|         body = ejs_1.default.render(JEST_CONFIG_EJS_TEMPLATE, { | ||||
|             exportKind: pkgJsonContent.type === 'module' ? 'export default' : 'module.exports =', | ||||
|             testEnvironment: jsdom ? 'jsdom' : 'node', | ||||
|             presetCreatorFn, | ||||
|             transformOpts: transformOpts ? ensureOnlyUsingDoubleQuotes(JSON.stringify(transformOpts, null, 2)) : undefined, | ||||
|         }); | ||||
|     } | ||||
|     (0, fs_1.writeFileSync)(filePath, body); | ||||
|     process.stderr.write(` | ||||
| Jest configuration written to "${filePath}". | ||||
| `); | ||||
| }; | ||||
| exports.run = run; | ||||
| /** | ||||
|  * @internal | ||||
|  */ | ||||
| const help = async () => { | ||||
|     process.stdout.write(` | ||||
| Usage: | ||||
|   ts-jest config:init [options] [<config-file>] | ||||
|  | ||||
| Arguments: | ||||
|   <config-file>         Can be a js or json Jest config file. If it is a | ||||
|                         package.json file, the configuration will be read from | ||||
|                         the "jest" property. | ||||
|                         Default: jest.config.js | ||||
|  | ||||
| Options: | ||||
|   --force               Discard any existing Jest config | ||||
|   --js ts|babel         Process '.js' files with ts-jest if 'ts' or with | ||||
|                         babel-jest if 'babel' | ||||
|   --no-jest-preset      Disable the use of Jest presets | ||||
|   --tsconfig <file>     Path to the tsconfig.json file | ||||
|   --babel               Enable using Babel to process 'js' resulted content from 'ts-jest' processing | ||||
|   --jsdom               Use 'jsdom' as test environment instead of 'node' | ||||
| `); | ||||
| }; | ||||
| exports.help = help; | ||||
							
								
								
									
										1
									
								
								frontend/node_modules/ts-jest/dist/cli/config/migrate.d.ts
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								frontend/node_modules/ts-jest/dist/cli/config/migrate.d.ts
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export {}; | ||||
							
								
								
									
										174
									
								
								frontend/node_modules/ts-jest/dist/cli/config/migrate.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								frontend/node_modules/ts-jest/dist/cli/config/migrate.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| "use strict"; | ||||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||||
|     return (mod && mod.__esModule) ? mod : { "default": mod }; | ||||
| }; | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| exports.help = exports.run = void 0; | ||||
| const fs_1 = require("fs"); | ||||
| const path_1 = require("path"); | ||||
| const bs_logger_1 = require("bs-logger"); | ||||
| const fast_json_stable_stringify_1 = __importDefault(require("fast-json-stable-stringify")); | ||||
| const json5_1 = require("json5"); | ||||
| const create_jest_preset_1 = require("../../presets/create-jest-preset"); | ||||
| const backports_1 = require("../../utils/backports"); | ||||
| const presets_1 = require("../helpers/presets"); | ||||
| const migrateGlobalConfigToTransformConfig = (transformConfig, globalsTsJestConfig) => { | ||||
|     if (transformConfig) { | ||||
|         return Object.entries(transformConfig).reduce((previousValue, currentValue) => { | ||||
|             const [key, transformOptions] = currentValue; | ||||
|             if (typeof transformOptions === 'string' && transformOptions.includes('ts-jest')) { | ||||
|                 return { | ||||
|                     ...previousValue, | ||||
|                     [key]: globalsTsJestConfig ? ['ts-jest', globalsTsJestConfig] : 'ts-jest', | ||||
|                 }; | ||||
|             } | ||||
|             return { | ||||
|                 ...previousValue, | ||||
|                 [key]: transformOptions, | ||||
|             }; | ||||
|         }, {}); | ||||
|     } | ||||
|     return {}; | ||||
| }; | ||||
| const migratePresetToTransformConfig = (transformConfig, preset, globalsTsJestConfig) => { | ||||
|     if (preset) { | ||||
|         const transformConfigFromPreset = preset.name === "ts-jest/presets/js-with-ts" /* JestPresetNames.jsWithTs */ | ||||
|             ? (0, create_jest_preset_1.createJsWithTsPreset)(globalsTsJestConfig) | ||||
|             : preset.name === "ts-jest/presets/js-with-babel" /* JestPresetNames.jsWIthBabel */ | ||||
|                 ? (0, create_jest_preset_1.createJsWithBabelPreset)(globalsTsJestConfig) | ||||
|                 : (0, create_jest_preset_1.createDefaultPreset)(globalsTsJestConfig); | ||||
|         return { | ||||
|             ...transformConfig, | ||||
|             ...transformConfigFromPreset.transform, | ||||
|         }; | ||||
|     } | ||||
|     return transformConfig; | ||||
| }; | ||||
| /** | ||||
|  * @internal | ||||
|  */ | ||||
| const run = async (args /* , logger: Logger*/) => { | ||||
|     const nullLogger = (0, bs_logger_1.createLogger)({ targets: [] }); | ||||
|     const file = args._[0]?.toString(); | ||||
|     const filePath = (0, path_1.resolve)(process.cwd(), file); | ||||
|     if (!(0, fs_1.existsSync)(filePath)) { | ||||
|         throw new Error(`Configuration file ${file} does not exists.`); | ||||
|     } | ||||
|     const name = (0, path_1.basename)(file); | ||||
|     const isPackage = name === 'package.json'; | ||||
|     if (!/\.(js|json)$/.test(name)) { | ||||
|         throw new TypeError(`Configuration file ${file} must be a JavaScript or JSON file.`); | ||||
|     } | ||||
|     let actualConfig = require(filePath); | ||||
|     if (isPackage) { | ||||
|         // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||||
|         actualConfig = actualConfig.jest; | ||||
|     } | ||||
|     if (!actualConfig) | ||||
|         actualConfig = {}; | ||||
|     // migrate | ||||
|     // first we backport our options | ||||
|     const migratedConfig = (0, backports_1.backportJestConfig)(nullLogger, actualConfig); | ||||
|     let preset; | ||||
|     if (migratedConfig.preset) { | ||||
|         // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||||
|         preset = presets_1.allPresets[migratedConfig.preset] ?? presets_1.allPresets["ts-jest/presets/default" /* JestPresetNames.default */]; | ||||
|     } | ||||
|     else { | ||||
|         if (args.js) { | ||||
|             preset = args.js === 'babel' ? presets_1.allPresets["ts-jest/presets/js-with-babel" /* JestPresetNames.jsWIthBabel */] : presets_1.allPresets["ts-jest/presets/js-with-ts" /* JestPresetNames.jsWithTs */]; | ||||
|         } | ||||
|         else { | ||||
|             preset = presets_1.allPresets["ts-jest/presets/default" /* JestPresetNames.default */]; | ||||
|         } | ||||
|     } | ||||
|     // check the extensions | ||||
|     if (migratedConfig.moduleFileExtensions?.length && preset) { | ||||
|         const presetValue = dedupSort(preset.value.moduleFileExtensions ?? []).join('::'); | ||||
|         const migratedValue = dedupSort(migratedConfig.moduleFileExtensions).join('::'); | ||||
|         if (presetValue === migratedValue) { | ||||
|             delete migratedConfig.moduleFileExtensions; | ||||
|         } | ||||
|     } | ||||
|     // there is a testRegex, remove our testMatch | ||||
|     if (typeof migratedConfig.testRegex === 'string' || migratedConfig.testRegex?.length) { | ||||
|         delete migratedConfig.testMatch; | ||||
|     } | ||||
|     // check the testMatch | ||||
|     else if (migratedConfig.testMatch?.length && preset) { | ||||
|         const presetValue = dedupSort(preset.value.testMatch ?? []).join('::'); | ||||
|         const migratedValue = dedupSort(migratedConfig.testMatch).join('::'); | ||||
|         if (presetValue === migratedValue) { | ||||
|             delete migratedConfig.testMatch; | ||||
|         } | ||||
|     } | ||||
|     const globalsTsJestConfig = migratedConfig.globals?.['ts-jest']; | ||||
|     migratedConfig.transform = migrateGlobalConfigToTransformConfig(migratedConfig.transform, globalsTsJestConfig); | ||||
|     migratedConfig.transform = migratePresetToTransformConfig(migratedConfig.transform, preset, globalsTsJestConfig); | ||||
|     cleanupConfig(actualConfig); | ||||
|     cleanupConfig(migratedConfig); | ||||
|     const before = (0, fast_json_stable_stringify_1.default)(actualConfig); | ||||
|     const after = (0, fast_json_stable_stringify_1.default)(migratedConfig); | ||||
|     if (after === before) { | ||||
|         process.stderr.write(` | ||||
| No migration needed for given Jest configuration | ||||
|     `); | ||||
|         return; | ||||
|     } | ||||
|     const stringify = file.endsWith('.json') ? JSON.stringify : json5_1.stringify; | ||||
|     const prefix = file.endsWith('.json') ? '"jest": ' : 'module.exports = '; | ||||
|     // output new config | ||||
|     process.stderr.write(` | ||||
| Migrated Jest configuration: | ||||
| `); | ||||
|     process.stdout.write(`${prefix}${stringify(migratedConfig, undefined, '  ')}\n`); | ||||
| }; | ||||
| exports.run = run; | ||||
| function cleanupConfig(config) { | ||||
|     if (config.globals) { | ||||
|         delete config.globals['ts-jest']; | ||||
|         if (!Object.keys(config.globals).length) { | ||||
|             delete config.globals; | ||||
|         } | ||||
|     } | ||||
|     if (config.transform && !Object.keys(config.transform).length) { | ||||
|         delete config.transform; | ||||
|     } | ||||
|     if (config.moduleFileExtensions) { | ||||
|         config.moduleFileExtensions = dedupSort(config.moduleFileExtensions); | ||||
|         if (!config.moduleFileExtensions.length) | ||||
|             delete config.moduleFileExtensions; | ||||
|     } | ||||
|     if (config.testMatch) { | ||||
|         config.testMatch = dedupSort(config.testMatch); | ||||
|         if (!config.testMatch.length) | ||||
|             delete config.testMatch; | ||||
|     } | ||||
|     delete config.preset; | ||||
| } | ||||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||||
| function dedupSort(arr) { | ||||
|     return arr | ||||
|         .filter((s, i, a) => a.findIndex((e) => s.toString() === e.toString()) === i) | ||||
|         .sort((a, b) => (a.toString() > b.toString() ? 1 : a.toString() < b.toString() ? -1 : 0)); | ||||
| } | ||||
| /** | ||||
|  * @internal | ||||
|  */ | ||||
| const help = async () => { | ||||
|     process.stdout.write(` | ||||
| Usage: | ||||
|   ts-jest config:migrate [options] <config-file> | ||||
|  | ||||
| Arguments: | ||||
|   <config-file>         Can be a js or json Jest config file. If it is a | ||||
|                         package.json file, the configuration will be read from | ||||
|                         the "jest" property. | ||||
|  | ||||
| Options: | ||||
|   --js ts|babel         Process .js files with ts-jest if 'ts' or with | ||||
|                         babel-jest if 'babel' | ||||
|   --no-jest-preset      Disable the use of Jest presets | ||||
| `); | ||||
| }; | ||||
| exports.help = help; | ||||
		Reference in New Issue
	
	Block a user
	 anthonyrawlins
					anthonyrawlins