 aacb45156b
			
		
	
	aacb45156b
	
	
	
		
			
			- 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>
		
			
				
	
	
		
			254 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import { createRequire } from "node:module";
 | |
| import exit from "exit-x";
 | |
| import HasteMap from "jest-haste-map";
 | |
| import { formatExecError, separateMessageFromStack } from "jest-message-util";
 | |
| import Runtime from "jest-runtime";
 | |
| import { messageParent } from "jest-worker";
 | |
| import { runInContext } from "node:vm";
 | |
| import chalk from "chalk";
 | |
| import * as fs from "graceful-fs";
 | |
| import * as sourcemapSupport from "source-map-support";
 | |
| import { BufferedConsole, CustomConsole, NullConsole, getConsoleOutput } from "@jest/console";
 | |
| import { createScriptTransformer } from "@jest/transform";
 | |
| import * as docblock from "jest-docblock";
 | |
| import LeakDetector from "jest-leak-detector";
 | |
| import { resolveTestEnvironment } from "jest-resolve";
 | |
| import { ErrorWithStack, interopRequireDefault, setGlobal } from "jest-util";
 | |
| 
 | |
| //#region rolldown:runtime
 | |
| var __require = /* @__PURE__ */ createRequire(import.meta.url);
 | |
| 
 | |
| //#endregion
 | |
| //#region src/runTest.ts
 | |
| function freezeConsole(testConsole, config) {
 | |
| 	testConsole._log = function fakeConsolePush(_type, message) {
 | |
| 		const error = new ErrorWithStack(`${chalk.red(`${chalk.bold("Cannot log after tests are done.")} Did you forget to wait for something async in your test?`)}\nAttempted to log "${message}".`, fakeConsolePush);
 | |
| 		const formattedError = formatExecError(error, config, { noStackTrace: false }, void 0, true);
 | |
| 		process.stderr.write(`\n${formattedError}\n`);
 | |
| 		process.exitCode = 1;
 | |
| 	};
 | |
| }
 | |
| async function runTestInternal(path, globalConfig, projectConfig, resolver, context, sendMessageToJest$1) {
 | |
| 	const testSource = fs.readFileSync(path, "utf8");
 | |
| 	const docblockPragmas = docblock.parse(docblock.extract(testSource));
 | |
| 	const customEnvironment = docblockPragmas["jest-environment"];
 | |
| 	const loadTestEnvironmentStart = Date.now();
 | |
| 	let testEnvironment = projectConfig.testEnvironment;
 | |
| 	if (customEnvironment) {
 | |
| 		if (Array.isArray(customEnvironment)) throw new TypeError(`You can only define a single test environment through docblocks, got "${customEnvironment.join(", ")}"`);
 | |
| 		testEnvironment = resolveTestEnvironment({
 | |
| 			...projectConfig,
 | |
| 			requireResolveFunction: (module) => __require.resolve(module),
 | |
| 			testEnvironment: customEnvironment
 | |
| 		});
 | |
| 	}
 | |
| 	const cacheFS = new Map([[path, testSource]]);
 | |
| 	const transformer = await createScriptTransformer(projectConfig, cacheFS);
 | |
| 	const TestEnvironment = await transformer.requireAndTranspileModule(testEnvironment);
 | |
| 	const testFramework = await transformer.requireAndTranspileModule(process.env.JEST_JASMINE === "1" ? __require.resolve("jest-jasmine2") : projectConfig.testRunner);
 | |
| 	const Runtime$1 = interopRequireDefault(projectConfig.runtime ? __require(projectConfig.runtime) : __require("jest-runtime")).default;
 | |
| 	const consoleOut = globalConfig.useStderr ? process.stderr : process.stdout;
 | |
| 	const consoleFormatter = (type, message) => getConsoleOutput(BufferedConsole.write([], type, message, 4), projectConfig, globalConfig);
 | |
| 	let testConsole;
 | |
| 	if (globalConfig.silent) testConsole = new NullConsole(consoleOut, consoleOut, consoleFormatter);
 | |
| 	else if (globalConfig.verbose) testConsole = new CustomConsole(consoleOut, consoleOut, consoleFormatter);
 | |
| 	else testConsole = new BufferedConsole();
 | |
| 	let extraTestEnvironmentOptions;
 | |
| 	const docblockEnvironmentOptions = docblockPragmas["jest-environment-options"];
 | |
| 	if (typeof docblockEnvironmentOptions === "string") extraTestEnvironmentOptions = JSON.parse(docblockEnvironmentOptions);
 | |
| 	const environment = new TestEnvironment({
 | |
| 		globalConfig,
 | |
| 		projectConfig: extraTestEnvironmentOptions ? {
 | |
| 			...projectConfig,
 | |
| 			testEnvironmentOptions: {
 | |
| 				...projectConfig.testEnvironmentOptions,
 | |
| 				...extraTestEnvironmentOptions
 | |
| 			}
 | |
| 		} : projectConfig
 | |
| 	}, {
 | |
| 		console: testConsole,
 | |
| 		docblockPragmas,
 | |
| 		testPath: path
 | |
| 	});
 | |
| 	const loadTestEnvironmentEnd = Date.now();
 | |
| 	if (typeof environment.getVmContext !== "function") {
 | |
| 		console.error(`Test environment found at "${testEnvironment}" does not export a "getVmContext" method, which is mandatory from Jest 27. This method is a replacement for "runScript".`);
 | |
| 		process.exit(1);
 | |
| 	}
 | |
| 	const leakDetector = projectConfig.detectLeaks ? new LeakDetector(environment) : null;
 | |
| 	setGlobal(environment.global, "console", testConsole, "retain");
 | |
| 	const runtime = new Runtime$1(projectConfig, environment, resolver, transformer, cacheFS, {
 | |
| 		changedFiles: context.changedFiles,
 | |
| 		collectCoverage: globalConfig.collectCoverage,
 | |
| 		collectCoverageFrom: globalConfig.collectCoverageFrom,
 | |
| 		coverageProvider: globalConfig.coverageProvider,
 | |
| 		sourcesRelatedToTestsInChangedFiles: context.sourcesRelatedToTestsInChangedFiles
 | |
| 	}, path, globalConfig);
 | |
| 	let isTornDown = false;
 | |
| 	const tearDownEnv = async () => {
 | |
| 		if (!isTornDown) {
 | |
| 			runtime.teardown();
 | |
| 			runInContext("Error.prepareStackTrace = () => '';", environment.getVmContext());
 | |
| 			sourcemapSupport.resetRetrieveHandlers();
 | |
| 			await environment.teardown();
 | |
| 			isTornDown = true;
 | |
| 		}
 | |
| 	};
 | |
| 	const start = Date.now();
 | |
| 	const setupFilesStart = Date.now();
 | |
| 	for (const path$1 of projectConfig.setupFiles) {
 | |
| 		const esm = runtime.unstable_shouldLoadAsEsm(path$1);
 | |
| 		if (esm) await runtime.unstable_importModule(path$1);
 | |
| 		else {
 | |
| 			const setupFile = runtime.requireModule(path$1);
 | |
| 			if (typeof setupFile === "function") await setupFile();
 | |
| 		}
 | |
| 	}
 | |
| 	const setupFilesEnd = Date.now();
 | |
| 	const sourcemapOptions = {
 | |
| 		environment: "node",
 | |
| 		handleUncaughtExceptions: false,
 | |
| 		retrieveSourceMap: (source) => {
 | |
| 			const sourceMapSource = runtime.getSourceMaps()?.get(source);
 | |
| 			if (sourceMapSource) try {
 | |
| 				return {
 | |
| 					map: JSON.parse(fs.readFileSync(sourceMapSource, "utf8")),
 | |
| 					url: source
 | |
| 				};
 | |
| 			} catch {}
 | |
| 			return null;
 | |
| 		}
 | |
| 	};
 | |
| 	runtime.requireInternalModule(__require.resolve("source-map-support")).install(sourcemapOptions);
 | |
| 	sourcemapSupport.install(sourcemapOptions);
 | |
| 	if (environment.global && environment.global.process && environment.global.process.exit) {
 | |
| 		const realExit = environment.global.process.exit;
 | |
| 		environment.global.process.exit = function exit$1(...args) {
 | |
| 			const error = new ErrorWithStack(`process.exit called with "${args.join(", ")}"`, exit$1);
 | |
| 			const formattedError = formatExecError(error, projectConfig, { noStackTrace: false }, void 0, true);
 | |
| 			process.stderr.write(formattedError);
 | |
| 			return realExit(...args);
 | |
| 		};
 | |
| 	}
 | |
| 	const collectV8Coverage = globalConfig.collectCoverage && globalConfig.coverageProvider === "v8" && typeof environment.getVmContext === "function";
 | |
| 	Error.stackTraceLimit = 100;
 | |
| 	try {
 | |
| 		await environment.setup();
 | |
| 		let result;
 | |
| 		try {
 | |
| 			if (collectV8Coverage) await runtime.collectV8Coverage();
 | |
| 			result = await testFramework(globalConfig, projectConfig, environment, runtime, path, sendMessageToJest$1);
 | |
| 		} catch (error) {
 | |
| 			let e = error;
 | |
| 			while (typeof e === "object" && e !== null && "stack" in e) {
 | |
| 				e.stack;
 | |
| 				e = e?.cause;
 | |
| 			}
 | |
| 			throw error;
 | |
| 		} finally {
 | |
| 			if (collectV8Coverage) await runtime.stopCollectingV8Coverage();
 | |
| 		}
 | |
| 		freezeConsole(testConsole, projectConfig);
 | |
| 		const testCount = result.numPassingTests + result.numFailingTests + result.numPendingTests + result.numTodoTests;
 | |
| 		const end = Date.now();
 | |
| 		const testRuntime = end - start;
 | |
| 		result.perfStats = {
 | |
| 			...result.perfStats,
 | |
| 			end,
 | |
| 			loadTestEnvironmentEnd,
 | |
| 			loadTestEnvironmentStart,
 | |
| 			runtime: testRuntime,
 | |
| 			setupFilesEnd,
 | |
| 			setupFilesStart,
 | |
| 			slow: testRuntime / 1e3 > projectConfig.slowTestThreshold,
 | |
| 			start
 | |
| 		};
 | |
| 		result.testFilePath = path;
 | |
| 		result.console = testConsole.getBuffer();
 | |
| 		result.skipped = testCount === result.numPendingTests;
 | |
| 		result.displayName = projectConfig.displayName;
 | |
| 		const coverage = runtime.getAllCoverageInfoCopy();
 | |
| 		if (coverage) {
 | |
| 			const coverageKeys = Object.keys(coverage);
 | |
| 			if (coverageKeys.length > 0) result.coverage = coverage;
 | |
| 		}
 | |
| 		if (collectV8Coverage) {
 | |
| 			const v8Coverage = runtime.getAllV8CoverageInfoCopy();
 | |
| 			if (v8Coverage && v8Coverage.length > 0) result.v8Coverage = v8Coverage;
 | |
| 		}
 | |
| 		if (globalConfig.logHeapUsage) {
 | |
| 			globalThis.gc?.();
 | |
| 			result.memoryUsage = process.memoryUsage().heapUsed;
 | |
| 		}
 | |
| 		await tearDownEnv();
 | |
| 		return await new Promise((resolve) => {
 | |
| 			setImmediate(() => resolve({
 | |
| 				leakDetector,
 | |
| 				result
 | |
| 			}));
 | |
| 		});
 | |
| 	} finally {
 | |
| 		await tearDownEnv();
 | |
| 	}
 | |
| }
 | |
| async function runTest(path, globalConfig, config, resolver, context, sendMessageToJest$1) {
 | |
| 	const { leakDetector, result } = await runTestInternal(path, globalConfig, config, resolver, context, sendMessageToJest$1);
 | |
| 	if (leakDetector) {
 | |
| 		await new Promise((resolve) => setTimeout(resolve, 100));
 | |
| 		result.leaks = await leakDetector.isLeaking();
 | |
| 	} else result.leaks = false;
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| //#endregion
 | |
| //#region src/testWorker.ts
 | |
| process.on("uncaughtException", (err) => {
 | |
| 	if (err.stack) console.error(err.stack);
 | |
| 	else console.error(err);
 | |
| 	exit(1);
 | |
| });
 | |
| const formatError = (error) => {
 | |
| 	if (typeof error === "string") {
 | |
| 		const { message, stack } = separateMessageFromStack(error);
 | |
| 		return {
 | |
| 			message,
 | |
| 			stack,
 | |
| 			type: "Error"
 | |
| 		};
 | |
| 	}
 | |
| 	return {
 | |
| 		code: error.code || void 0,
 | |
| 		message: error.message,
 | |
| 		stack: error.stack,
 | |
| 		type: "Error"
 | |
| 	};
 | |
| };
 | |
| const resolvers = /* @__PURE__ */ new Map();
 | |
| const getResolver = (config) => {
 | |
| 	const resolver = resolvers.get(config.id);
 | |
| 	if (!resolver) throw new Error(`Cannot find resolver for: ${config.id}`);
 | |
| 	return resolver;
 | |
| };
 | |
| function setup(setupData) {
 | |
| 	for (const { config, serializableModuleMap } of setupData.serializableResolvers) {
 | |
| 		const moduleMap = HasteMap.getStatic(config).getModuleMapFromJSON(serializableModuleMap);
 | |
| 		resolvers.set(config.id, Runtime.createResolver(config, moduleMap));
 | |
| 	}
 | |
| }
 | |
| const sendMessageToJest = (eventName, args) => {
 | |
| 	messageParent([eventName, args]);
 | |
| };
 | |
| async function worker({ config, globalConfig, path, context }) {
 | |
| 	try {
 | |
| 		return await runTest(path, globalConfig, config, getResolver(config), {
 | |
| 			...context,
 | |
| 			changedFiles: context.changedFiles && new Set(context.changedFiles),
 | |
| 			sourcesRelatedToTestsInChangedFiles: context.sourcesRelatedToTestsInChangedFiles && new Set(context.sourcesRelatedToTestsInChangedFiles)
 | |
| 		}, sendMessageToJest);
 | |
| 	} catch (error) {
 | |
| 		throw formatError(error);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| //#endregion
 | |
| export { setup, worker }; |