 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>
		
			
				
	
	
		
			167 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			167 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| const fs = require('fs');
 | |
| const path = require('path');
 | |
| const {promisify} = require('util');
 | |
| const camelcase = require('camelcase');
 | |
| const findUp = require('find-up');
 | |
| const resolveFrom = require('resolve-from');
 | |
| const getPackageType = require('get-package-type');
 | |
| 
 | |
| const readFile = promisify(fs.readFile);
 | |
| 
 | |
| let loadActive = false;
 | |
| 
 | |
| function isLoading() {
 | |
| 	return loadActive;
 | |
| }
 | |
| 
 | |
| const standardConfigFiles = [
 | |
| 	'.nycrc',
 | |
| 	'.nycrc.json',
 | |
| 	'.nycrc.yml',
 | |
| 	'.nycrc.yaml',
 | |
| 	'nyc.config.js',
 | |
| 	'nyc.config.cjs',
 | |
| 	'nyc.config.mjs'
 | |
| ];
 | |
| 
 | |
| function camelcasedConfig(config) {
 | |
| 	const results = {};
 | |
| 	for (const [field, value] of Object.entries(config)) {
 | |
| 		results[camelcase(field)] = value;
 | |
| 	}
 | |
| 
 | |
| 	return results;
 | |
| }
 | |
| 
 | |
| async function findPackage(options) {
 | |
| 	const cwd = options.cwd || process.env.NYC_CWD || process.cwd();
 | |
| 	const pkgPath = await findUp('package.json', {cwd});
 | |
| 	if (pkgPath) {
 | |
| 		const pkgConfig = JSON.parse(await readFile(pkgPath, 'utf8')).nyc || {};
 | |
| 		if ('cwd' in pkgConfig) {
 | |
| 			pkgConfig.cwd = path.resolve(path.dirname(pkgPath), pkgConfig.cwd);
 | |
| 		}
 | |
| 
 | |
| 		return {
 | |
| 			cwd: path.dirname(pkgPath),
 | |
| 			pkgConfig
 | |
| 		};
 | |
| 	}
 | |
| 
 | |
| 	return {
 | |
| 		cwd,
 | |
| 		pkgConfig: {}
 | |
| 	};
 | |
| }
 | |
| 
 | |
| async function actualLoad(configFile) {
 | |
| 	if (!configFile) {
 | |
| 		return {};
 | |
| 	}
 | |
| 
 | |
| 	const configExt = path.extname(configFile).toLowerCase();
 | |
| 	switch (configExt) {
 | |
| 		case '.js':
 | |
| 			/* istanbul ignore next: coverage for 13.2.0+ is shown in load-esm.js */
 | |
| 			if (await getPackageType(configFile) === 'module') {
 | |
| 				return require('./load-esm')(configFile);
 | |
| 			}
 | |
| 
 | |
| 			/* fallthrough */
 | |
| 		case '.cjs':
 | |
| 			return require(configFile);
 | |
| 		/* istanbul ignore next: coverage for 13.2.0+ is shown in load-esm.js */
 | |
| 		case '.mjs':
 | |
| 			return require('./load-esm')(configFile);
 | |
| 		case '.yml':
 | |
| 		case '.yaml':
 | |
| 			return require('js-yaml').load(await readFile(configFile, 'utf8'));
 | |
| 		default:
 | |
| 			return JSON.parse(await readFile(configFile, 'utf8'));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| async function loadFile(configFile) {
 | |
| 	/* This lets @istanbuljs/esm-loader-hook avoid circular initialization when loading
 | |
| 	 * configuration.  This should generally only happen when the loader hook is active
 | |
| 	 * on the main nyc process. */
 | |
| 	loadActive = true;
 | |
| 
 | |
| 	try {
 | |
| 		return await actualLoad(configFile);
 | |
| 	} finally {
 | |
| 		loadActive = false;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| async function applyExtends(config, filename, loopCheck = new Set()) {
 | |
| 	config = camelcasedConfig(config);
 | |
| 	if ('extends' in config) {
 | |
| 		const extConfigs = [].concat(config.extends);
 | |
| 		if (extConfigs.some(e => typeof e !== 'string')) {
 | |
| 			throw new TypeError(`${filename} contains an invalid 'extends' option`);
 | |
| 		}
 | |
| 
 | |
| 		delete config.extends;
 | |
| 		const filePath = path.dirname(filename);
 | |
| 		for (const extConfig of extConfigs) {
 | |
| 			const configFile = resolveFrom.silent(filePath, extConfig) ||
 | |
| 				resolveFrom.silent(filePath, './' + extConfig);
 | |
| 			if (!configFile) {
 | |
| 				throw new Error(`Could not resolve configuration file ${extConfig} from ${path.dirname(filename)}.`);
 | |
| 			}
 | |
| 
 | |
| 			if (loopCheck.has(configFile)) {
 | |
| 				throw new Error(`Circular extended configurations: '${configFile}'.`);
 | |
| 			}
 | |
| 
 | |
| 			loopCheck.add(configFile);
 | |
| 
 | |
| 			// eslint-disable-next-line no-await-in-loop
 | |
| 			const configLoaded = await loadFile(configFile);
 | |
| 			if ('cwd' in configLoaded) {
 | |
| 				configLoaded.cwd = path.resolve(path.dirname(configFile), configLoaded.cwd);
 | |
| 			}
 | |
| 
 | |
| 			Object.assign(
 | |
| 				config,
 | |
| 				// eslint-disable-next-line no-await-in-loop
 | |
| 				await applyExtends(configLoaded, configFile, loopCheck)
 | |
| 			);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return config;
 | |
| }
 | |
| 
 | |
| async function loadNycConfig(options = {}) {
 | |
| 	const {cwd, pkgConfig} = await findPackage(options);
 | |
| 	const configFiles = [].concat(options.nycrcPath || standardConfigFiles);
 | |
| 	const configFile = await findUp(configFiles, {cwd});
 | |
| 	if (options.nycrcPath && !configFile) {
 | |
| 		throw new Error(`Requested configuration file ${options.nycrcPath} not found`);
 | |
| 	}
 | |
| 
 | |
| 	const config = {
 | |
| 		cwd,
 | |
| 		...(await applyExtends(pkgConfig, path.join(cwd, 'package.json'))),
 | |
| 		...(await applyExtends(await loadFile(configFile), configFile))
 | |
| 	};
 | |
| 
 | |
| 	const arrayFields = ['require', 'extension', 'exclude', 'include'];
 | |
| 	for (const arrayField of arrayFields) {
 | |
| 		if (config[arrayField]) {
 | |
| 			config[arrayField] = [].concat(config[arrayField]);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return config;
 | |
| }
 | |
| 
 | |
| module.exports = {
 | |
| 	loadNycConfig,
 | |
| 	isLoading
 | |
| };
 |