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:
anthonyrawlins
2025-07-11 14:06:34 +10:00
parent c6d69695a8
commit aacb45156b
6109 changed files with 777927 additions and 1 deletions

View File

@@ -0,0 +1,25 @@
import { SnapshotResolver } from "jest-snapshot";
import { IHasteFS } from "jest-haste-map";
import Resolver, { ResolveModuleConfig } from "jest-resolve";
//#region src/index.d.ts
type ResolvedModule = {
file: string;
dependencies: Array<string>;
};
/**
* DependencyResolver is used to resolve the direct dependencies of a module or
* to retrieve a list of all transitive inverse dependencies.
*/
declare class DependencyResolver {
private readonly _hasteFS;
private readonly _resolver;
private readonly _snapshotResolver;
constructor(resolver: Resolver, hasteFS: IHasteFS, snapshotResolver: SnapshotResolver);
resolve(file: string, options?: ResolveModuleConfig): Array<string>;
resolveInverseModuleMap(paths: Set<string>, filter: (file: string) => boolean, options?: ResolveModuleConfig): Array<ResolvedModule>;
resolveInverse(paths: Set<string>, filter: (file: string) => boolean, options?: ResolveModuleConfig): Array<string>;
}
//#endregion
export { DependencyResolver, ResolvedModule };

View File

@@ -0,0 +1,43 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {IHasteFS} from 'jest-haste-map';
import default_2, {ResolveModuleConfig} from 'jest-resolve';
import {SnapshotResolver} from 'jest-snapshot';
/**
* DependencyResolver is used to resolve the direct dependencies of a module or
* to retrieve a list of all transitive inverse dependencies.
*/
export declare class DependencyResolver {
private readonly _hasteFS;
private readonly _resolver;
private readonly _snapshotResolver;
constructor(
resolver: default_2,
hasteFS: IHasteFS,
snapshotResolver: SnapshotResolver,
);
resolve(file: string, options?: ResolveModuleConfig): Array<string>;
resolveInverseModuleMap(
paths: Set<string>,
filter: (file: string) => boolean,
options?: ResolveModuleConfig,
): Array<ResolvedModule>;
resolveInverse(
paths: Set<string>,
filter: (file: string) => boolean,
options?: ResolveModuleConfig,
): Array<string>;
}
export declare type ResolvedModule = {
file: string;
dependencies: Array<string>;
};
export {};

View File

@@ -0,0 +1,159 @@
/*!
* /**
* * Copyright (c) Meta Platforms, Inc. and affiliates.
* *
* * This source code is licensed under the MIT license found in the
* * LICENSE file in the root directory of this source tree.
* * /
*/
/******/ (() => { // webpackBootstrap
/******/ "use strict";
var __webpack_exports__ = {};
// This entry needs to be wrapped in an IIFE because it uses a non-standard name for the exports (exports).
(() => {
var exports = __webpack_exports__;
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports.DependencyResolver = void 0;
function path() {
const data = _interopRequireWildcard(require("path"));
path = function () {
return data;
};
return data;
}
function _jestSnapshot() {
const data = require("jest-snapshot");
_jestSnapshot = function () {
return data;
};
return data;
}
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* DependencyResolver is used to resolve the direct dependencies of a module or
* to retrieve a list of all transitive inverse dependencies.
*/
class DependencyResolver {
_hasteFS;
_resolver;
_snapshotResolver;
constructor(resolver, hasteFS, snapshotResolver) {
this._resolver = resolver;
this._hasteFS = hasteFS;
this._snapshotResolver = snapshotResolver;
}
resolve(file, options) {
const dependencies = this._hasteFS.getDependencies(file);
const fallbackOptions = {
conditions: undefined
};
if (!dependencies) {
return [];
}
return dependencies.reduce((acc, dependency) => {
if (this._resolver.isCoreModule(dependency)) {
return acc;
}
let resolvedDependency;
let resolvedMockDependency;
try {
resolvedDependency = this._resolver.resolveModule(file, dependency, options ?? fallbackOptions);
} catch {
try {
resolvedDependency = this._resolver.getMockModule(file, dependency, options ?? fallbackOptions);
} catch {
// leave resolvedDependency as undefined if nothing can be found
}
}
if (resolvedDependency == null) {
return acc;
}
acc.push(resolvedDependency);
// If we resolve a dependency, then look for a mock dependency
// of the same name in that dependency's directory.
try {
resolvedMockDependency = this._resolver.getMockModule(resolvedDependency, path().basename(dependency), options ?? fallbackOptions);
} catch {
// leave resolvedMockDependency as undefined if nothing can be found
}
if (resolvedMockDependency != null) {
const dependencyMockDir = path().resolve(path().dirname(resolvedDependency), '__mocks__');
resolvedMockDependency = path().resolve(resolvedMockDependency);
// make sure mock is in the correct directory
if (dependencyMockDir === path().dirname(resolvedMockDependency)) {
acc.push(resolvedMockDependency);
}
}
return acc;
}, []);
}
resolveInverseModuleMap(paths, filter, options) {
if (paths.size === 0) {
return [];
}
const collectModules = (related, moduleMap, changed) => {
const visitedModules = new Set();
const result = [];
while (changed.size > 0) {
changed = new Set(moduleMap.reduce((acc, module) => {
if (visitedModules.has(module.file) || !module.dependencies.some(dep => changed.has(dep))) {
return acc;
}
const file = module.file;
if (filter(file)) {
result.push(module);
related.delete(file);
}
visitedModules.add(file);
acc.push(file);
return acc;
}, []));
}
return [...result, ...[...related].map(file => ({
dependencies: [],
file
}))];
};
const relatedPaths = new Set();
const changed = new Set();
for (const path of paths) {
if (this._hasteFS.exists(path)) {
const modulePath = (0, _jestSnapshot().isSnapshotPath)(path) ? this._snapshotResolver.resolveTestPath(path) : path;
changed.add(modulePath);
if (filter(modulePath)) {
relatedPaths.add(modulePath);
}
}
}
const modules = [];
for (const file of this._hasteFS.getAbsoluteFileIterator()) {
modules.push({
dependencies: this.resolve(file, options),
file
});
}
return collectModules(relatedPaths, modules, changed);
}
resolveInverse(paths, filter, options) {
return this.resolveInverseModuleMap(paths, filter, options).map(module => module.file);
}
}
exports.DependencyResolver = DependencyResolver;
})();
module.exports = __webpack_exports__;
/******/ })()
;

View File

@@ -0,0 +1,3 @@
import cjsModule from './index.js';
export const DependencyResolver = cjsModule.DependencyResolver;