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:
132
frontend/node_modules/@jest/pattern/src/TestPathPatterns.ts
generated
vendored
Normal file
132
frontend/node_modules/@jest/pattern/src/TestPathPatterns.ts
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* 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 * as path from 'path';
|
||||
import {replacePathSepForRegex} from 'jest-regex-util';
|
||||
|
||||
export class TestPathPatterns {
|
||||
constructor(readonly patterns: Array<string>) {}
|
||||
|
||||
/**
|
||||
* Return true if there are any patterns.
|
||||
*/
|
||||
isSet(): boolean {
|
||||
return this.patterns.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the patterns are valid.
|
||||
*/
|
||||
isValid(): boolean {
|
||||
return this.toExecutor({
|
||||
// isValid() doesn't require rootDir to be accurate, so just
|
||||
// specify a dummy rootDir here
|
||||
rootDir: '/',
|
||||
}).isValid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a human-friendly version of the pattern regex.
|
||||
*/
|
||||
toPretty(): string {
|
||||
return this.patterns.join('|');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a TestPathPatternsExecutor that can execute the patterns.
|
||||
*/
|
||||
toExecutor(
|
||||
options: TestPathPatternsExecutorOptions,
|
||||
): TestPathPatternsExecutor {
|
||||
return new TestPathPatternsExecutor(this, options);
|
||||
}
|
||||
|
||||
/** For jest serializers */
|
||||
toJSON(): any {
|
||||
return {
|
||||
patterns: this.patterns,
|
||||
type: 'TestPathPatterns',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export type TestPathPatternsExecutorOptions = {
|
||||
rootDir: string;
|
||||
};
|
||||
|
||||
export class TestPathPatternsExecutor {
|
||||
constructor(
|
||||
readonly patterns: TestPathPatterns,
|
||||
private readonly options: TestPathPatternsExecutorOptions,
|
||||
) {}
|
||||
|
||||
private toRegex(s: string): RegExp {
|
||||
return new RegExp(s, 'i');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are any patterns.
|
||||
*/
|
||||
isSet(): boolean {
|
||||
return this.patterns.isSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the patterns are valid.
|
||||
*/
|
||||
isValid(): boolean {
|
||||
try {
|
||||
for (const p of this.patterns.patterns) {
|
||||
this.toRegex(p);
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given ABSOLUTE path matches the patterns.
|
||||
*
|
||||
* Throws an error if the patterns form an invalid regex (see `validate`).
|
||||
*/
|
||||
isMatch(absPath: string): boolean {
|
||||
const relPath = path.relative(this.options.rootDir || '/', absPath);
|
||||
|
||||
if (this.patterns.patterns.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const p of this.patterns.patterns) {
|
||||
const pathToTest = path.isAbsolute(p) ? absPath : relPath;
|
||||
|
||||
// special case: ./foo.spec.js (and .\foo.spec.js on Windows) should
|
||||
// match /^foo.spec.js/ after stripping root dir
|
||||
let regexStr = p.replace(/^\.\//, '^');
|
||||
if (path.sep === '\\') {
|
||||
regexStr = regexStr.replace(/^\.\\/, '^');
|
||||
}
|
||||
|
||||
regexStr = replacePathSepForRegex(regexStr);
|
||||
if (this.toRegex(regexStr).test(pathToTest)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.toRegex(regexStr).test(absPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a human-friendly version of the pattern regex.
|
||||
*/
|
||||
toPretty(): string {
|
||||
return this.patterns.toPretty();
|
||||
}
|
||||
}
|
||||
259
frontend/node_modules/@jest/pattern/src/__tests__/TestPathPatterns.test.ts
generated
vendored
Normal file
259
frontend/node_modules/@jest/pattern/src/__tests__/TestPathPatterns.test.ts
generated
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
/**
|
||||
* 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 * as path from 'path';
|
||||
import {
|
||||
TestPathPatterns,
|
||||
TestPathPatternsExecutor,
|
||||
type TestPathPatternsExecutorOptions,
|
||||
} from '../TestPathPatterns';
|
||||
|
||||
const mockSep: jest.Mock<() => string> = jest.fn();
|
||||
const mockIsAbsolute: jest.Mock<(p: string) => boolean> = jest.fn();
|
||||
const mockRelative: jest.Mock<(from: string, to: string) => string> = jest.fn();
|
||||
jest.mock('path', () => {
|
||||
const actualPath = jest.requireActual('path');
|
||||
return {
|
||||
...actualPath,
|
||||
isAbsolute(p) {
|
||||
return mockIsAbsolute(p) || actualPath.isAbsolute(p);
|
||||
},
|
||||
relative(from, to) {
|
||||
return mockRelative(from, to) || actualPath.relative(from, to);
|
||||
},
|
||||
get sep() {
|
||||
return mockSep() || actualPath.sep;
|
||||
},
|
||||
} as typeof path;
|
||||
});
|
||||
const forcePosix = () => {
|
||||
mockSep.mockReturnValue(path.posix.sep);
|
||||
mockIsAbsolute.mockImplementation(path.posix.isAbsolute);
|
||||
mockRelative.mockImplementation(path.posix.relative);
|
||||
};
|
||||
const forceWindows = () => {
|
||||
mockSep.mockReturnValue(path.win32.sep);
|
||||
mockIsAbsolute.mockImplementation(path.win32.isAbsolute);
|
||||
mockRelative.mockImplementation(path.win32.relative);
|
||||
};
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
forcePosix();
|
||||
});
|
||||
|
||||
const config = {rootDir: ''};
|
||||
|
||||
interface TestPathPatternsLike {
|
||||
isSet(): boolean;
|
||||
isValid(): boolean;
|
||||
toPretty(): string;
|
||||
}
|
||||
|
||||
const testPathPatternsLikeTests = (
|
||||
makePatterns: (
|
||||
patterns: Array<string>,
|
||||
options: TestPathPatternsExecutorOptions,
|
||||
) => TestPathPatternsLike,
|
||||
) => {
|
||||
describe('isSet', () => {
|
||||
it('returns false if no patterns specified', () => {
|
||||
const testPathPatterns = makePatterns([], config);
|
||||
expect(testPathPatterns.isSet()).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true if patterns specified', () => {
|
||||
const testPathPatterns = makePatterns(['a'], config);
|
||||
expect(testPathPatterns.isSet()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isValid', () => {
|
||||
it('succeeds for empty patterns', () => {
|
||||
const testPathPatterns = makePatterns([], config);
|
||||
expect(testPathPatterns.isValid()).toBe(true);
|
||||
});
|
||||
|
||||
it('succeeds for valid patterns', () => {
|
||||
const testPathPatterns = makePatterns(['abc+', 'z.*'], config);
|
||||
expect(testPathPatterns.isValid()).toBe(true);
|
||||
});
|
||||
|
||||
it('fails for at least one invalid pattern', () => {
|
||||
const testPathPatterns = makePatterns(['abc+', '(', 'z.*'], config);
|
||||
expect(testPathPatterns.isValid()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toPretty', () => {
|
||||
it('renders a human-readable string', () => {
|
||||
const testPathPatterns = makePatterns(['a/b', 'c/d'], config);
|
||||
expect(testPathPatterns.toPretty()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
describe('TestPathPatterns', () => {
|
||||
testPathPatternsLikeTests(
|
||||
(patterns: Array<string>, _: TestPathPatternsExecutorOptions) =>
|
||||
new TestPathPatterns(patterns),
|
||||
);
|
||||
});
|
||||
|
||||
describe('TestPathPatternsExecutor', () => {
|
||||
const makeExecutor = (
|
||||
patterns: Array<string>,
|
||||
options: TestPathPatternsExecutorOptions,
|
||||
) => new TestPathPatternsExecutor(new TestPathPatterns(patterns), options);
|
||||
|
||||
testPathPatternsLikeTests(makeExecutor);
|
||||
|
||||
describe('isMatch', () => {
|
||||
it('returns true with no patterns', () => {
|
||||
const testPathPatterns = makeExecutor([], config);
|
||||
expect(testPathPatterns.isMatch('/a/b')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for same path', () => {
|
||||
const testPathPatterns = makeExecutor(['/a/b'], config);
|
||||
expect(testPathPatterns.isMatch('/a/b')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for same path with case insensitive', () => {
|
||||
const testPathPatternsUpper = makeExecutor(['/A/B'], config);
|
||||
expect(testPathPatternsUpper.isMatch('/a/b')).toBe(true);
|
||||
expect(testPathPatternsUpper.isMatch('/A/B')).toBe(true);
|
||||
|
||||
const testPathPatternsLower = makeExecutor(['/a/b'], config);
|
||||
expect(testPathPatternsLower.isMatch('/A/B')).toBe(true);
|
||||
expect(testPathPatternsLower.isMatch('/a/b')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for contained path', () => {
|
||||
const testPathPatterns = makeExecutor(['b/c'], config);
|
||||
expect(testPathPatterns.isMatch('/a/b/c/d')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for explicit relative path', () => {
|
||||
const testPathPatterns = makeExecutor(['./b/c'], {
|
||||
rootDir: '/a',
|
||||
});
|
||||
expect(testPathPatterns.isMatch('/a/b/c')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for explicit relative path for Windows with ./', () => {
|
||||
forceWindows();
|
||||
const testPathPatterns = makeExecutor(['./b/c'], {
|
||||
rootDir: 'C:\\a',
|
||||
});
|
||||
expect(testPathPatterns.isMatch('C:\\a\\b\\c')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for explicit relative path for Windows with .\\', () => {
|
||||
forceWindows();
|
||||
const testPathPatterns = makeExecutor(['.\\b\\c'], {
|
||||
rootDir: 'C:\\a',
|
||||
});
|
||||
expect(testPathPatterns.isMatch('C:\\a\\b\\c')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for partial file match', () => {
|
||||
const testPathPatterns = makeExecutor(['aaa'], config);
|
||||
expect(testPathPatterns.isMatch('/foo/..aaa..')).toBe(true);
|
||||
expect(testPathPatterns.isMatch('/foo/..aaa')).toBe(true);
|
||||
expect(testPathPatterns.isMatch('/foo/aaa..')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for path suffix', () => {
|
||||
const testPathPatterns = makeExecutor(['c/d'], config);
|
||||
expect(testPathPatterns.isMatch('/a/b/c/d')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true if regex matches', () => {
|
||||
const testPathPatterns = makeExecutor(['ab*c?'], config);
|
||||
|
||||
expect(testPathPatterns.isMatch('/foo/a')).toBe(true);
|
||||
expect(testPathPatterns.isMatch('/foo/ab')).toBe(true);
|
||||
expect(testPathPatterns.isMatch('/foo/abb')).toBe(true);
|
||||
expect(testPathPatterns.isMatch('/foo/ac')).toBe(true);
|
||||
expect(testPathPatterns.isMatch('/foo/abc')).toBe(true);
|
||||
expect(testPathPatterns.isMatch('/foo/abbc')).toBe(true);
|
||||
|
||||
expect(testPathPatterns.isMatch('/foo/bc')).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true only if matches relative path', () => {
|
||||
const rootDir = '/home/myuser/';
|
||||
|
||||
const testPathPatterns = makeExecutor(['home'], {
|
||||
rootDir,
|
||||
});
|
||||
expect(
|
||||
testPathPatterns.isMatch(
|
||||
path.relative(rootDir, '/home/myuser/LoginPage.js'),
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
testPathPatterns.isMatch(
|
||||
path.relative(rootDir, '/home/myuser/HomePage.js'),
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('matches absolute paths regardless of rootDir', () => {
|
||||
forcePosix();
|
||||
const testPathPatterns = makeExecutor(['/a/b'], {
|
||||
rootDir: '/foo/bar',
|
||||
});
|
||||
expect(testPathPatterns.isMatch('/a/b')).toBe(true);
|
||||
});
|
||||
|
||||
it('matches absolute paths for Windows', () => {
|
||||
forceWindows();
|
||||
const testPathPatterns = makeExecutor(['C:\\a\\b'], {
|
||||
rootDir: 'C:\\foo\\bar',
|
||||
});
|
||||
expect(testPathPatterns.isMatch('C:\\a\\b')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true if match any paths', () => {
|
||||
const testPathPatterns = makeExecutor(['a/b', 'c/d'], config);
|
||||
|
||||
expect(testPathPatterns.isMatch('/foo/a/b')).toBe(true);
|
||||
expect(testPathPatterns.isMatch('/foo/c/d')).toBe(true);
|
||||
|
||||
expect(testPathPatterns.isMatch('/foo/a')).toBe(false);
|
||||
expect(testPathPatterns.isMatch('/foo/b/c')).toBe(false);
|
||||
});
|
||||
|
||||
it('does not normalize Windows paths on POSIX', () => {
|
||||
forcePosix();
|
||||
const testPathPatterns = makeExecutor(['a\\z', 'a\\\\z'], config);
|
||||
expect(testPathPatterns.isMatch('/foo/a/z')).toBe(false);
|
||||
});
|
||||
|
||||
it('normalizes paths for Windows', () => {
|
||||
forceWindows();
|
||||
const testPathPatterns = makeExecutor(['a/b'], config);
|
||||
expect(testPathPatterns.isMatch('C:\\foo\\a\\b')).toBe(true);
|
||||
});
|
||||
|
||||
it('matches absolute path with absPath', () => {
|
||||
const pattern = '^/home/app/';
|
||||
const rootDir = '/home/app';
|
||||
const absolutePath = '/home/app/packages/';
|
||||
|
||||
const testPathPatterns = makeExecutor([pattern], {
|
||||
rootDir,
|
||||
});
|
||||
|
||||
const relativePath = path.relative(rootDir, absolutePath);
|
||||
|
||||
expect(testPathPatterns.isMatch(relativePath)).toBe(false);
|
||||
expect(testPathPatterns.isMatch(absolutePath)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
5
frontend/node_modules/@jest/pattern/src/__tests__/__snapshots__/TestPathPatterns.test.ts.snap
generated
vendored
Normal file
5
frontend/node_modules/@jest/pattern/src/__tests__/__snapshots__/TestPathPatterns.test.ts.snap
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`TestPathPatterns toPretty renders a human-readable string 1`] = `"a/b|c/d"`;
|
||||
|
||||
exports[`TestPathPatternsExecutor toPretty renders a human-readable string 1`] = `"a/b|c/d"`;
|
||||
12
frontend/node_modules/@jest/pattern/src/index.ts
generated
vendored
Normal file
12
frontend/node_modules/@jest/pattern/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export {
|
||||
TestPathPatterns,
|
||||
TestPathPatternsExecutor,
|
||||
type TestPathPatternsExecutorOptions,
|
||||
} from './TestPathPatterns';
|
||||
Reference in New Issue
Block a user