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:
		
							
								
								
									
										293
									
								
								frontend/node_modules/ts-jest/dist/legacy/ts-jest-transformer.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								frontend/node_modules/ts-jest/dist/legacy/ts-jest-transformer.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,293 @@ | ||||
| "use strict"; | ||||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||||
|     return (mod && mod.__esModule) ? mod : { "default": mod }; | ||||
| }; | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| exports.TsJestTransformer = exports.CACHE_KEY_EL_SEPARATOR = void 0; | ||||
| const fs_1 = require("fs"); | ||||
| const path_1 = __importDefault(require("path")); | ||||
| const typescript_1 = __importDefault(require("typescript")); | ||||
| const constants_1 = require("../constants"); | ||||
| const utils_1 = require("../utils"); | ||||
| const importer_1 = require("../utils/importer"); | ||||
| const messages_1 = require("../utils/messages"); | ||||
| const sha1_1 = require("../utils/sha1"); | ||||
| const compiler_1 = require("./compiler"); | ||||
| const compiler_utils_1 = require("./compiler/compiler-utils"); | ||||
| const config_set_1 = require("./config/config-set"); | ||||
| const isNodeModule = (filePath) => { | ||||
|     return path_1.default.normalize(filePath).split(path_1.default.sep).includes('node_modules'); | ||||
| }; | ||||
| /** | ||||
|  * @internal | ||||
|  */ | ||||
| exports.CACHE_KEY_EL_SEPARATOR = '\x00'; | ||||
| class TsJestTransformer { | ||||
|     transformerOptions; | ||||
|     /** | ||||
|      * cache ConfigSet between test runs | ||||
|      * | ||||
|      * @internal | ||||
|      */ | ||||
|     static _cachedConfigSets = []; | ||||
|     _logger; | ||||
|     _compiler; | ||||
|     _transformCfgStr; | ||||
|     _depGraphs = new Map(); | ||||
|     _watchMode = false; | ||||
|     constructor(transformerOptions) { | ||||
|         this.transformerOptions = transformerOptions; | ||||
|         this._logger = utils_1.rootLogger.child({ namespace: 'ts-jest-transformer' }); | ||||
|         /** | ||||
|          * For some unknown reasons, `this` is undefined in `getCacheKey` and `process` | ||||
|          * when running Jest in ESM mode | ||||
|          */ | ||||
|         this.getCacheKey = this.getCacheKey.bind(this); | ||||
|         this.getCacheKeyAsync = this.getCacheKeyAsync.bind(this); | ||||
|         this.process = this.process.bind(this); | ||||
|         this.processAsync = this.processAsync.bind(this); | ||||
|         this._logger.debug('created new transformer'); | ||||
|         process.env.TS_JEST = '1'; | ||||
|     } | ||||
|     _configsFor(transformOptions) { | ||||
|         const { config, cacheFS } = transformOptions; | ||||
|         const ccs = TsJestTransformer._cachedConfigSets.find((cs) => cs.jestConfig.value === config); | ||||
|         let configSet; | ||||
|         if (ccs) { | ||||
|             this._transformCfgStr = ccs.transformerCfgStr; | ||||
|             this._compiler = ccs.compiler; | ||||
|             this._depGraphs = ccs.depGraphs; | ||||
|             this._watchMode = ccs.watchMode; | ||||
|             configSet = ccs.configSet; | ||||
|         } | ||||
|         else { | ||||
|             // try to look-it up by stringified version | ||||
|             const serializedJestCfg = (0, utils_1.stringify)(config); | ||||
|             const serializedCcs = TsJestTransformer._cachedConfigSets.find((cs) => cs.jestConfig.serialized === serializedJestCfg); | ||||
|             if (serializedCcs) { | ||||
|                 // update the object so that we can find it later | ||||
|                 // this happens because jest first calls getCacheKey with stringified version of | ||||
|                 // the config, and then it calls the transformer with the proper object | ||||
|                 serializedCcs.jestConfig.value = config; | ||||
|                 this._transformCfgStr = serializedCcs.transformerCfgStr; | ||||
|                 this._compiler = serializedCcs.compiler; | ||||
|                 this._depGraphs = serializedCcs.depGraphs; | ||||
|                 this._watchMode = serializedCcs.watchMode; | ||||
|                 configSet = serializedCcs.configSet; | ||||
|             } | ||||
|             else { | ||||
|                 // create the new record in the index | ||||
|                 this._logger.info('no matching config-set found, creating a new one'); | ||||
|                 if (config.globals?.['ts-jest']) { | ||||
|                     this._logger.warn("Define `ts-jest` config under `globals` is deprecated. Please do\ntransform: {\n    <transform_regex>: ['ts-jest', { /* ts-jest config goes here in Jest */ }],\n},\nSee more at https://kulshekhar.github.io/ts-jest/docs/getting-started/presets#advanced" /* Deprecations.GlobalsTsJestConfigOption */); | ||||
|                 } | ||||
|                 const jestGlobalsConfig = config.globals ?? {}; | ||||
|                 const tsJestGlobalsConfig = jestGlobalsConfig['ts-jest'] ?? {}; | ||||
|                 const migratedConfig = this.transformerOptions | ||||
|                     ? { | ||||
|                         ...config, | ||||
|                         globals: { | ||||
|                             ...jestGlobalsConfig, | ||||
|                             'ts-jest': { | ||||
|                                 ...tsJestGlobalsConfig, | ||||
|                                 ...this.transformerOptions, | ||||
|                             }, | ||||
|                         }, | ||||
|                     } | ||||
|                     : config; | ||||
|                 configSet = this._createConfigSet(migratedConfig); | ||||
|                 const jest = { ...migratedConfig }; | ||||
|                 // we need to remove some stuff from jest config | ||||
|                 // this which does not depend on config | ||||
|                 jest.cacheDirectory = undefined; // eslint-disable-line @typescript-eslint/no-explicit-any | ||||
|                 this._transformCfgStr = `${new utils_1.JsonableValue(jest).serialized}${configSet.cacheSuffix}`; | ||||
|                 this._createCompiler(configSet, cacheFS); | ||||
|                 this._watchMode = process.argv.includes('--watch'); | ||||
|                 TsJestTransformer._cachedConfigSets.push({ | ||||
|                     jestConfig: new utils_1.JsonableValue(config), | ||||
|                     configSet, | ||||
|                     transformerCfgStr: this._transformCfgStr, | ||||
|                     compiler: this._compiler, | ||||
|                     depGraphs: this._depGraphs, | ||||
|                     watchMode: this._watchMode, | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|         return configSet; | ||||
|     } | ||||
|     _createConfigSet(config) { | ||||
|         return new config_set_1.ConfigSet(config); | ||||
|     } | ||||
|     _createCompiler(configSet, cacheFS) { | ||||
|         this._compiler = new compiler_1.TsJestCompiler(configSet, cacheFS); | ||||
|     } | ||||
|     process(sourceText, sourcePath, transformOptions) { | ||||
|         this._logger.debug({ fileName: sourcePath, transformOptions }, 'processing', sourcePath); | ||||
|         const configs = this._configsFor(transformOptions); | ||||
|         const shouldStringifyContent = configs.shouldStringifyContent(sourcePath); | ||||
|         const babelJest = shouldStringifyContent ? undefined : configs.babelJestTransformer; | ||||
|         let result = { | ||||
|             code: this.processWithTs(sourceText, sourcePath, transformOptions).code, | ||||
|         }; | ||||
|         if (babelJest) { | ||||
|             this._logger.debug({ fileName: sourcePath }, 'calling babel-jest processor'); | ||||
|             // do not instrument here, jest will do it anyway afterwards | ||||
|             result = babelJest.process(result.code, sourcePath, { | ||||
|                 ...transformOptions, | ||||
|                 instrument: false, | ||||
|             }); | ||||
|         } | ||||
|         result = this.runTsJestHook(sourcePath, sourceText, transformOptions, result); | ||||
|         return result; | ||||
|     } | ||||
|     async processAsync(sourceText, sourcePath, transformOptions) { | ||||
|         this._logger.debug({ fileName: sourcePath, transformOptions }, 'processing', sourcePath); | ||||
|         const configs = this._configsFor(transformOptions); | ||||
|         const shouldStringifyContent = configs.shouldStringifyContent(sourcePath); | ||||
|         const babelJest = shouldStringifyContent ? undefined : configs.babelJestTransformer; | ||||
|         let result; | ||||
|         const processWithTsResult = this.processWithTs(sourceText, sourcePath, transformOptions); | ||||
|         result = { | ||||
|             code: processWithTsResult.code, | ||||
|         }; | ||||
|         if (processWithTsResult.diagnostics?.length) { | ||||
|             throw configs.createTsError(processWithTsResult.diagnostics); | ||||
|         } | ||||
|         if (babelJest) { | ||||
|             this._logger.debug({ fileName: sourcePath }, 'calling babel-jest processor'); | ||||
|             // do not instrument here, jest will do it anyway afterwards | ||||
|             result = await babelJest.processAsync(result.code, sourcePath, { | ||||
|                 ...transformOptions, | ||||
|                 instrument: false, | ||||
|             }); | ||||
|         } | ||||
|         result = this.runTsJestHook(sourcePath, sourceText, transformOptions, result); | ||||
|         return result; | ||||
|     } | ||||
|     processWithTs(sourceText, sourcePath, transformOptions) { | ||||
|         let result; | ||||
|         const configs = this._configsFor(transformOptions); | ||||
|         const shouldStringifyContent = configs.shouldStringifyContent(sourcePath); | ||||
|         const babelJest = shouldStringifyContent ? undefined : configs.babelJestTransformer; | ||||
|         const isDefinitionFile = sourcePath.endsWith(constants_1.DECLARATION_TYPE_EXT); | ||||
|         const isJsFile = constants_1.JS_JSX_REGEX.test(sourcePath); | ||||
|         const isTsFile = !isDefinitionFile && constants_1.TS_TSX_REGEX.test(sourcePath); | ||||
|         if (shouldStringifyContent) { | ||||
|             // handles here what we should simply stringify | ||||
|             result = { | ||||
|                 code: `module.exports=${(0, utils_1.stringify)(sourceText)}`, | ||||
|             }; | ||||
|         } | ||||
|         else if (isDefinitionFile) { | ||||
|             // do not try to compile declaration files | ||||
|             result = { | ||||
|                 code: '', | ||||
|             }; | ||||
|         } | ||||
|         else if (isJsFile || isTsFile) { | ||||
|             if (isJsFile && isNodeModule(sourcePath)) { | ||||
|                 const transpiledResult = typescript_1.default.transpileModule(sourceText, { | ||||
|                     compilerOptions: { | ||||
|                         ...configs.parsedTsConfig.options, | ||||
|                         module: transformOptions.supportsStaticESM && transformOptions.transformerConfig.useESM | ||||
|                             ? typescript_1.default.ModuleKind.ESNext | ||||
|                             : typescript_1.default.ModuleKind.CommonJS, | ||||
|                     }, | ||||
|                     fileName: sourcePath, | ||||
|                 }); | ||||
|                 result = { | ||||
|                     code: (0, compiler_utils_1.updateOutput)(transpiledResult.outputText, sourcePath, transpiledResult.sourceMapText), | ||||
|                 }; | ||||
|             } | ||||
|             else { | ||||
|                 // transpile TS code (source maps are included) | ||||
|                 result = this._compiler.getCompiledOutput(sourceText, sourcePath, { | ||||
|                     depGraphs: this._depGraphs, | ||||
|                     supportsStaticESM: transformOptions.supportsStaticESM, | ||||
|                     watchMode: this._watchMode, | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             // we should not get called for files with other extension than js[x], ts[x] and d.ts, | ||||
|             // TypeScript will bail if we try to compile, and if it was to call babel, users can | ||||
|             // define the transform value with `babel-jest` for this extension instead | ||||
|             const message = babelJest ? "Got a unknown file type to compile (file: {{path}}). To fix this, in your Jest config change the `transform` key which value is `ts-jest` so that it does not match this kind of files anymore. If you still want Babel to process it, add another entry to the `transform` option with value `babel-jest` which key matches this type of files." /* Errors.GotUnknownFileTypeWithBabel */ : "Got a unknown file type to compile (file: {{path}}). To fix this, in your Jest config change the `transform` key which value is `ts-jest` so that it does not match this kind of files anymore." /* Errors.GotUnknownFileTypeWithoutBabel */; | ||||
|             this._logger.warn({ fileName: sourcePath }, (0, messages_1.interpolate)(message, { path: sourcePath })); | ||||
|             result = { | ||||
|                 code: sourceText, | ||||
|             }; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|     runTsJestHook(sourcePath, sourceText, transformOptions, compiledOutput) { | ||||
|         let hooksFile = process.env.TS_JEST_HOOKS; | ||||
|         let hooks; | ||||
|         /* istanbul ignore next (cover by e2e) */ | ||||
|         if (hooksFile) { | ||||
|             hooksFile = path_1.default.resolve(this._configsFor(transformOptions).cwd, hooksFile); | ||||
|             hooks = importer_1.importer.tryTheseOr(hooksFile, {}); | ||||
|         } | ||||
|         // This is not supposed to be a public API but we keep it as some people use it | ||||
|         if (hooks?.afterProcess) { | ||||
|             this._logger.debug({ fileName: sourcePath, hookName: 'afterProcess' }, 'calling afterProcess hook'); | ||||
|             const newResult = hooks.afterProcess([sourceText, sourcePath, transformOptions.config, transformOptions], compiledOutput); | ||||
|             if (newResult) { | ||||
|                 return newResult; | ||||
|             } | ||||
|         } | ||||
|         return compiledOutput; | ||||
|     } | ||||
|     /** | ||||
|      * Jest uses this to cache the compiled version of a file | ||||
|      * | ||||
|      * @see https://github.com/facebook/jest/blob/v23.5.0/packages/jest-runtime/src/script_transformer.js#L61-L90 | ||||
|      * | ||||
|      * @public | ||||
|      */ | ||||
|     getCacheKey(fileContent, filePath, transformOptions) { | ||||
|         const configs = this._configsFor(transformOptions); | ||||
|         this._logger.debug({ fileName: filePath, transformOptions }, 'computing cache key for', filePath); | ||||
|         // we do not instrument, ensure it is false all the time | ||||
|         const { supportsStaticESM, instrument = false } = transformOptions; | ||||
|         const constructingCacheKeyElements = [ | ||||
|             this._transformCfgStr, | ||||
|             exports.CACHE_KEY_EL_SEPARATOR, | ||||
|             configs.rootDir, | ||||
|             exports.CACHE_KEY_EL_SEPARATOR, | ||||
|             `instrument:${instrument ? 'on' : 'off'}`, | ||||
|             exports.CACHE_KEY_EL_SEPARATOR, | ||||
|             `supportsStaticESM:${supportsStaticESM ? 'on' : 'off'}`, | ||||
|             exports.CACHE_KEY_EL_SEPARATOR, | ||||
|             fileContent, | ||||
|             exports.CACHE_KEY_EL_SEPARATOR, | ||||
|             filePath, | ||||
|         ]; | ||||
|         if (!configs.isolatedModules && configs.tsCacheDir) { | ||||
|             let resolvedModuleNames; | ||||
|             if (this._depGraphs.get(filePath)?.fileContent === fileContent) { | ||||
|                 this._logger.debug({ fileName: filePath, transformOptions }, 'getting resolved modules from disk caching or memory caching for', filePath); | ||||
|                 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||||
|                 resolvedModuleNames = this._depGraphs | ||||
|                     .get(filePath) | ||||
|                     .resolvedModuleNames.filter((moduleName) => (0, fs_1.existsSync)(moduleName)); | ||||
|             } | ||||
|             else { | ||||
|                 this._logger.debug({ fileName: filePath, transformOptions }, 'getting resolved modules from TypeScript API for', filePath); | ||||
|                 resolvedModuleNames = this._compiler.getResolvedModules(fileContent, filePath, transformOptions.cacheFS); | ||||
|                 this._depGraphs.set(filePath, { | ||||
|                     fileContent, | ||||
|                     resolvedModuleNames, | ||||
|                 }); | ||||
|             } | ||||
|             resolvedModuleNames.forEach((moduleName) => { | ||||
|                 constructingCacheKeyElements.push(exports.CACHE_KEY_EL_SEPARATOR, moduleName, exports.CACHE_KEY_EL_SEPARATOR, (0, fs_1.statSync)(moduleName).mtimeMs.toString()); | ||||
|             }); | ||||
|         } | ||||
|         return (0, sha1_1.sha1)(...constructingCacheKeyElements); | ||||
|     } | ||||
|     async getCacheKeyAsync(sourceText, sourcePath, transformOptions) { | ||||
|         return Promise.resolve(this.getCacheKey(sourceText, sourcePath, transformOptions)); | ||||
|     } | ||||
| } | ||||
| exports.TsJestTransformer = TsJestTransformer; | ||||
		Reference in New Issue
	
	Block a user
	 anthonyrawlins
					anthonyrawlins