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

22
frontend/node_modules/@jest/snapshot-utils/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,5 @@
{
"extends": "../../api-extractor.json",
"mainEntryPointFilePath": "/Users/cpojer/Dropbox/Projects/jest/packages/jest-snapshot-utils/build/index.d.ts",
"projectFolder": "/Users/cpojer/Dropbox/Projects/jest/packages/jest-snapshot-utils"
}

View File

@@ -0,0 +1,29 @@
import { Config } from "@jest/types";
//#region src/types.d.ts
/**
* 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.
*/
type SnapshotData = Record<string, string>;
//#endregion
//#region src/utils.d.ts
declare const SNAPSHOT_VERSION = "1";
declare const SNAPSHOT_GUIDE_LINK = "https://jestjs.io/docs/snapshot-testing";
declare const SNAPSHOT_VERSION_WARNING: string;
declare const testNameToKey: (testName: string, count: number) => string;
declare const keyToTestName: (key: string) => string;
declare const getSnapshotData: (snapshotPath: string, update: Config.SnapshotUpdateState) => {
data: SnapshotData;
dirty: boolean;
};
declare const escapeBacktickString: (str: string) => string;
declare const ensureDirectoryExists: (filePath: string) => void;
declare const normalizeNewlines: (string: string) => string;
declare const saveSnapshotFile: (snapshotData: SnapshotData, snapshotPath: string) => void;
//#endregion
export { SNAPSHOT_GUIDE_LINK, SNAPSHOT_VERSION, SNAPSHOT_VERSION_WARNING, SnapshotData, ensureDirectoryExists, escapeBacktickString, getSnapshotData, keyToTestName, normalizeNewlines, saveSnapshotFile, testNameToKey };

View File

@@ -0,0 +1,42 @@
/**
* 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 {Config} from '@jest/types';
export declare const ensureDirectoryExists: (filePath: string) => void;
export declare const escapeBacktickString: (str: string) => string;
export declare const getSnapshotData: (
snapshotPath: string,
update: Config.SnapshotUpdateState,
) => {
data: SnapshotData;
dirty: boolean;
};
export declare const keyToTestName: (key: string) => string;
export declare const normalizeNewlines: (string: string) => string;
export declare const saveSnapshotFile: (
snapshotData: SnapshotData,
snapshotPath: string,
) => void;
export declare const SNAPSHOT_GUIDE_LINK =
'https://jestjs.io/docs/snapshot-testing';
export declare const SNAPSHOT_VERSION = '1';
export declare const SNAPSHOT_VERSION_WARNING: string;
export declare type SnapshotData = Record<string, string>;
export declare const testNameToKey: (testName: string, count: number) => string;
export {};

View File

@@ -0,0 +1,221 @@
/*!
* /**
* * 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_modules__ = ({
/***/ "./src/index.ts":
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
var _utils = __webpack_require__("./src/utils.ts");
Object.keys(_utils).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _utils[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _utils[key];
}
});
});
var _types = __webpack_require__("./src/types.ts");
Object.keys(_types).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _types[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _types[key];
}
});
});
/***/ }),
/***/ "./src/types.ts":
/***/ (() => {
/***/ }),
/***/ "./src/utils.ts":
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports.testNameToKey = exports.saveSnapshotFile = exports.normalizeNewlines = exports.keyToTestName = exports.getSnapshotData = exports.escapeBacktickString = exports.ensureDirectoryExists = exports.SNAPSHOT_VERSION_WARNING = exports.SNAPSHOT_VERSION = exports.SNAPSHOT_GUIDE_LINK = void 0;
var path = _interopRequireWildcard(require("path"));
var _chalk = _interopRequireDefault(require("chalk"));
var fs = _interopRequireWildcard(require("graceful-fs"));
var _naturalCompare = _interopRequireDefault(require("natural-compare"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
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); }
var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
var jestWriteFile = globalThis[Symbol.for('jest-native-write-file')] || fs.writeFileSync;
var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
var jestReadFile = globalThis[Symbol.for('jest-native-read-file')] || fs.readFileSync;
var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
var jestExistsFile = globalThis[Symbol.for('jest-native-exists-file')] || fs.existsSync;
/**
* 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.
*/
const SNAPSHOT_VERSION = exports.SNAPSHOT_VERSION = '1';
const SNAPSHOT_VERSION_REGEXP = /^\/\/ Jest Snapshot v(.+),/;
const SNAPSHOT_GUIDE_LINK = exports.SNAPSHOT_GUIDE_LINK = 'https://jestjs.io/docs/snapshot-testing';
const SNAPSHOT_VERSION_WARNING = exports.SNAPSHOT_VERSION_WARNING = _chalk.default.yellow(`${_chalk.default.bold('Warning')}: Before you upgrade snapshots, ` + 'we recommend that you revert any local changes to tests or other code, ' + 'to ensure that you do not store invalid state.');
const writeSnapshotVersion = () => `// Jest Snapshot v${SNAPSHOT_VERSION}, ${SNAPSHOT_GUIDE_LINK}`;
const validateSnapshotVersion = snapshotContents => {
const versionTest = SNAPSHOT_VERSION_REGEXP.exec(snapshotContents);
const version = versionTest && versionTest[1];
if (!version) {
return new Error(_chalk.default.red(`${_chalk.default.bold('Outdated snapshot')}: No snapshot header found. ` + 'Jest 19 introduced versioned snapshots to ensure all developers ' + 'on a project are using the same version of Jest. ' + 'Please update all snapshots during this upgrade of Jest.\n\n') + SNAPSHOT_VERSION_WARNING);
}
if (version < SNAPSHOT_VERSION) {
return new Error(
// eslint-disable-next-line prefer-template
_chalk.default.red(`${_chalk.default.red.bold('Outdated snapshot')}: The version of the snapshot ` + 'file associated with this test is outdated. The snapshot file ' + 'version ensures that all developers on a project are using ' + 'the same version of Jest. ' + 'Please update all snapshots during this upgrade of Jest.') + '\n\n' + `Expected: v${SNAPSHOT_VERSION}\n` + `Received: v${version}\n\n` + SNAPSHOT_VERSION_WARNING);
}
if (version > SNAPSHOT_VERSION) {
return new Error(
// eslint-disable-next-line prefer-template
_chalk.default.red(`${_chalk.default.red.bold('Outdated Jest version')}: The version of this ` + 'snapshot file indicates that this project is meant to be used ' + 'with a newer version of Jest. The snapshot file version ensures ' + 'that all developers on a project are using the same version of ' + 'Jest. Please update your version of Jest and re-run the tests.') + '\n\n' + `Expected: v${SNAPSHOT_VERSION}\n` + `Received: v${version}`);
}
return null;
};
const normalizeTestNameForKey = testName => testName.replaceAll(/\r\n|\r|\n/g, match => {
switch (match) {
case '\r\n':
return '\\r\\n';
case '\r':
return '\\r';
case '\n':
return '\\n';
default:
return match;
}
});
const denormalizeTestNameFromKey = key => key.replaceAll(/\\r\\n|\\r|\\n/g, match => {
switch (match) {
case '\\r\\n':
return '\r\n';
case '\\r':
return '\r';
case '\\n':
return '\n';
default:
return match;
}
});
const testNameToKey = (testName, count) => `${normalizeTestNameForKey(testName)} ${count}`;
exports.testNameToKey = testNameToKey;
const keyToTestName = key => {
if (!/ \d+$/.test(key)) {
throw new Error('Snapshot keys must end with a number.');
}
const testNameWithoutCount = key.replace(/ \d+$/, '');
return denormalizeTestNameFromKey(testNameWithoutCount);
};
exports.keyToTestName = keyToTestName;
const getSnapshotData = (snapshotPath, update) => {
const data = Object.create(null);
let snapshotContents = '';
let dirty = false;
if (jestExistsFile(snapshotPath)) {
try {
snapshotContents = jestReadFile(snapshotPath, 'utf8');
// eslint-disable-next-line no-new-func
const populate = new Function('exports', snapshotContents);
populate(data);
} catch {}
}
const validationResult = validateSnapshotVersion(snapshotContents);
const isInvalid = snapshotContents && validationResult;
if (update === 'none' && isInvalid) {
throw validationResult;
}
if ((update === 'all' || update === 'new') && isInvalid) {
dirty = true;
}
return {
data,
dirty
};
};
exports.getSnapshotData = getSnapshotData;
const escapeBacktickString = str => str.replaceAll(/`|\\|\${/g, '\\$&');
exports.escapeBacktickString = escapeBacktickString;
const printBacktickString = str => `\`${escapeBacktickString(str)}\``;
const ensureDirectoryExists = filePath => {
try {
fs.mkdirSync(path.dirname(filePath), {
recursive: true
});
} catch {}
};
exports.ensureDirectoryExists = ensureDirectoryExists;
const normalizeNewlines = string => string.replaceAll(/\r\n|\r/g, '\n');
exports.normalizeNewlines = normalizeNewlines;
const saveSnapshotFile = (snapshotData, snapshotPath) => {
const snapshots = Object.keys(snapshotData).sort(_naturalCompare.default).map(key => `exports[${printBacktickString(key)}] = ${printBacktickString(normalizeNewlines(snapshotData[key]))};`);
ensureDirectoryExists(snapshotPath);
jestWriteFile(snapshotPath, `${writeSnapshotVersion()}\n\n${snapshots.join('\n\n')}\n`);
};
exports.saveSnapshotFile = saveSnapshotFile;
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module is referenced by other modules so it can't be inlined
/******/ var __webpack_exports__ = __webpack_require__("./src/index.ts");
/******/ module.exports = __webpack_exports__;
/******/
/******/ })()
;

View File

@@ -0,0 +1,12 @@
import cjsModule from './index.js';
export const SNAPSHOT_GUIDE_LINK = cjsModule.SNAPSHOT_GUIDE_LINK;
export const SNAPSHOT_VERSION = cjsModule.SNAPSHOT_VERSION;
export const SNAPSHOT_VERSION_WARNING = cjsModule.SNAPSHOT_VERSION_WARNING;
export const ensureDirectoryExists = cjsModule.ensureDirectoryExists;
export const escapeBacktickString = cjsModule.escapeBacktickString;
export const getSnapshotData = cjsModule.getSnapshotData;
export const keyToTestName = cjsModule.keyToTestName;
export const normalizeNewlines = cjsModule.normalizeNewlines;
export const saveSnapshotFile = cjsModule.saveSnapshotFile;
export const testNameToKey = cjsModule.testNameToKey;

View File

@@ -0,0 +1,38 @@
{
"name": "@jest/snapshot-utils",
"version": "30.0.4",
"repository": {
"type": "git",
"url": "https://github.com/jestjs/jest.git",
"directory": "packages/jest-snapshot-utils"
},
"license": "MIT",
"main": "./build/index.js",
"types": "./build/index.d.ts",
"exports": {
".": {
"types": "./build/index.d.ts",
"require": "./build/index.js",
"import": "./build/index.mjs",
"default": "./build/index.js"
},
"./package.json": "./package.json"
},
"dependencies": {
"@jest/types": "30.0.1",
"chalk": "^4.1.2",
"graceful-fs": "^4.2.11",
"natural-compare": "^1.4.0"
},
"devDependencies": {
"@types/graceful-fs": "^4.1.9",
"@types/natural-compare": "^1.4.3"
},
"engines": {
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
},
"publishConfig": {
"access": "public"
},
"gitHead": "f4296d2bc85c1405f84ddf613a25d0bc3766b7e5"
}

View File

@@ -0,0 +1,211 @@
/**
* 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.
*/
jest.mock('graceful-fs', () => ({
...jest.createMockFromModule<typeof import('fs')>('fs'),
existsSync: jest.fn().mockReturnValue(true),
}));
import * as path from 'path';
import chalk from 'chalk';
import * as fs from 'graceful-fs';
import {
SNAPSHOT_GUIDE_LINK,
SNAPSHOT_VERSION,
SNAPSHOT_VERSION_WARNING,
getSnapshotData,
keyToTestName,
saveSnapshotFile,
testNameToKey,
} from '../utils';
test('keyToTestName()', () => {
expect(keyToTestName('abc cde 12')).toBe('abc cde');
expect(keyToTestName('abc cde 12')).toBe('abc cde ');
expect(keyToTestName('test with\\r\\nCRLF 1')).toBe('test with\r\nCRLF');
expect(keyToTestName('test with\\rCR 1')).toBe('test with\rCR');
expect(keyToTestName('test with\\nLF 1')).toBe('test with\nLF');
expect(() => keyToTestName('abc cde')).toThrow(
'Snapshot keys must end with a number.',
);
});
test('testNameToKey', () => {
expect(testNameToKey('abc cde', 1)).toBe('abc cde 1');
expect(testNameToKey('abc cde ', 12)).toBe('abc cde 12');
});
test('testNameToKey escapes line endings to prevent collisions', () => {
expect(testNameToKey('test with\r\nCRLF', 1)).toBe('test with\\r\\nCRLF 1');
expect(testNameToKey('test with\rCR', 1)).toBe('test with\\rCR 1');
expect(testNameToKey('test with\nLF', 1)).toBe('test with\\nLF 1');
expect(testNameToKey('test\r\n', 1)).not.toBe(testNameToKey('test\r', 1));
expect(testNameToKey('test\r\n', 1)).not.toBe(testNameToKey('test\n', 1));
expect(testNameToKey('test\r', 1)).not.toBe(testNameToKey('test\n', 1));
});
test('keyToTestName reverses testNameToKey transformation', () => {
const testCases = [
'simple test',
'test with\r\nCRLF',
'test with\rCR only',
'test with\nLF only',
'mixed\r\nline\rendings\n',
'test\r',
'test\r\n',
'test\n',
];
for (const testName of testCases) {
const key = testNameToKey(testName, 1);
const recovered = keyToTestName(key);
expect(recovered).toBe(testName);
}
});
test('saveSnapshotFile() works with \r\n', () => {
const filename = path.join(__dirname, 'remove-newlines.snap');
const data = {
myKey: '<div>\r\n</div>',
};
saveSnapshotFile(data, filename);
expect(fs.writeFileSync).toHaveBeenCalledWith(
filename,
`// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` +
'exports[`myKey`] = `<div>\n</div>`;\n',
);
});
test('saveSnapshotFile() works with \r', () => {
const filename = path.join(__dirname, 'remove-newlines.snap');
const data = {
myKey: '<div>\r</div>',
};
saveSnapshotFile(data, filename);
expect(fs.writeFileSync).toHaveBeenCalledWith(
filename,
`// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` +
'exports[`myKey`] = `<div>\n</div>`;\n',
);
});
test('getSnapshotData() throws when no snapshot version', () => {
const filename = path.join(__dirname, 'old-snapshot.snap');
jest
.mocked(fs.readFileSync)
.mockReturnValue('exports[`myKey`] = `<div>\n</div>`;\n');
const update = 'none';
expect(() => getSnapshotData(filename, update)).toThrow(
chalk.red(
`${chalk.bold('Outdated snapshot')}: No snapshot header found. ` +
'Jest 19 introduced versioned snapshots to ensure all developers on ' +
'a project are using the same version of Jest. ' +
'Please update all snapshots during this upgrade of Jest.\n\n',
) + SNAPSHOT_VERSION_WARNING,
);
});
test('getSnapshotData() throws for older snapshot version', () => {
const filename = path.join(__dirname, 'old-snapshot.snap');
jest
.mocked(fs.readFileSync)
.mockReturnValue(
`// Jest Snapshot v0.99, ${SNAPSHOT_GUIDE_LINK}\n\n` +
'exports[`myKey`] = `<div>\n</div>`;\n',
);
const update = 'none';
expect(() => getSnapshotData(filename, update)).toThrow(
`${chalk.red(
`${chalk.red.bold('Outdated snapshot')}: The version of the snapshot ` +
'file associated with this test is outdated. The snapshot file ' +
'version ensures that all developers on a project are using ' +
'the same version of Jest. ' +
'Please update all snapshots during this upgrade of Jest.',
)}\n\nExpected: v${SNAPSHOT_VERSION}\n` +
`Received: v0.99\n\n${SNAPSHOT_VERSION_WARNING}`,
);
});
test('getSnapshotData() throws for newer snapshot version', () => {
const filename = path.join(__dirname, 'old-snapshot.snap');
jest
.mocked(fs.readFileSync)
.mockReturnValue(
`// Jest Snapshot v2, ${SNAPSHOT_GUIDE_LINK}\n\n` +
'exports[`myKey`] = `<div>\n</div>`;\n',
);
const update = 'none';
expect(() => getSnapshotData(filename, update)).toThrow(
`${chalk.red(
`${chalk.red.bold('Outdated Jest version')}: The version of this ` +
'snapshot file indicates that this project is meant to be used ' +
'with a newer version of Jest. ' +
'The snapshot file version ensures that all developers on a project ' +
'are using the same version of Jest. ' +
'Please update your version of Jest and re-run the tests.',
)}\n\nExpected: v${SNAPSHOT_VERSION}\nReceived: v2`,
);
});
test('getSnapshotData() does not throw for when updating', () => {
const filename = path.join(__dirname, 'old-snapshot.snap');
jest
.mocked(fs.readFileSync)
.mockReturnValue('exports[`myKey`] = `<div>\n</div>`;\n');
const update = 'all';
expect(() => getSnapshotData(filename, update)).not.toThrow();
});
test('getSnapshotData() marks invalid snapshot dirty when updating', () => {
const filename = path.join(__dirname, 'old-snapshot.snap');
jest
.mocked(fs.readFileSync)
.mockReturnValue('exports[`myKey`] = `<div>\n</div>`;\n');
const update = 'all';
expect(getSnapshotData(filename, update)).toMatchObject({dirty: true});
});
test('getSnapshotData() marks valid snapshot not dirty when updating', () => {
const filename = path.join(__dirname, 'old-snapshot.snap');
jest
.mocked(fs.readFileSync)
.mockReturnValue(
`// Jest Snapshot v${SNAPSHOT_VERSION}, ${SNAPSHOT_GUIDE_LINK}\n\n` +
'exports[`myKey`] = `<div>\n</div>`;\n',
);
const update = 'all';
expect(getSnapshotData(filename, update)).toMatchObject({dirty: false});
});
test('escaping', () => {
const filename = path.join(__dirname, 'escaping.snap');
const data = '"\'\\';
const writeFileSync = jest.mocked(fs.writeFileSync);
writeFileSync.mockReset();
saveSnapshotFile({key: data}, filename);
const writtenData = writeFileSync.mock.calls[0][1];
expect(writtenData).toBe(
`// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` +
'exports[`key`] = `"\'\\\\`;\n',
);
// eslint-disable-next-line no-eval
const readData = eval(`var exports = {}; ${writtenData} exports`);
expect(readData).toEqual({key: data});
const snapshotData = readData.key;
expect(data).toEqual(snapshotData);
});

View File

@@ -0,0 +1,9 @@
/**
* 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 * from './utils';
export * from './types';

View File

@@ -0,0 +1,8 @@
/**
* 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 type SnapshotData = Record<string, string>;

184
frontend/node_modules/@jest/snapshot-utils/src/utils.ts generated vendored Normal file
View File

@@ -0,0 +1,184 @@
/**
* 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 chalk from 'chalk';
import * as fs from 'graceful-fs';
import naturalCompare from 'natural-compare';
import type {Config} from '@jest/types';
import type {SnapshotData} from './types';
export const SNAPSHOT_VERSION = '1';
const SNAPSHOT_VERSION_REGEXP = /^\/\/ Jest Snapshot v(.+),/;
export const SNAPSHOT_GUIDE_LINK = 'https://jestjs.io/docs/snapshot-testing';
export const SNAPSHOT_VERSION_WARNING = chalk.yellow(
`${chalk.bold('Warning')}: Before you upgrade snapshots, ` +
'we recommend that you revert any local changes to tests or other code, ' +
'to ensure that you do not store invalid state.',
);
const writeSnapshotVersion = () =>
`// Jest Snapshot v${SNAPSHOT_VERSION}, ${SNAPSHOT_GUIDE_LINK}`;
const validateSnapshotVersion = (snapshotContents: string) => {
const versionTest = SNAPSHOT_VERSION_REGEXP.exec(snapshotContents);
const version = versionTest && versionTest[1];
if (!version) {
return new Error(
chalk.red(
`${chalk.bold('Outdated snapshot')}: No snapshot header found. ` +
'Jest 19 introduced versioned snapshots to ensure all developers ' +
'on a project are using the same version of Jest. ' +
'Please update all snapshots during this upgrade of Jest.\n\n',
) + SNAPSHOT_VERSION_WARNING,
);
}
if (version < SNAPSHOT_VERSION) {
return new Error(
// eslint-disable-next-line prefer-template
chalk.red(
`${chalk.red.bold('Outdated snapshot')}: The version of the snapshot ` +
'file associated with this test is outdated. The snapshot file ' +
'version ensures that all developers on a project are using ' +
'the same version of Jest. ' +
'Please update all snapshots during this upgrade of Jest.',
) +
'\n\n' +
`Expected: v${SNAPSHOT_VERSION}\n` +
`Received: v${version}\n\n` +
SNAPSHOT_VERSION_WARNING,
);
}
if (version > SNAPSHOT_VERSION) {
return new Error(
// eslint-disable-next-line prefer-template
chalk.red(
`${chalk.red.bold('Outdated Jest version')}: The version of this ` +
'snapshot file indicates that this project is meant to be used ' +
'with a newer version of Jest. The snapshot file version ensures ' +
'that all developers on a project are using the same version of ' +
'Jest. Please update your version of Jest and re-run the tests.',
) +
'\n\n' +
`Expected: v${SNAPSHOT_VERSION}\n` +
`Received: v${version}`,
);
}
return null;
};
const normalizeTestNameForKey = (testName: string): string =>
testName.replaceAll(/\r\n|\r|\n/g, match => {
switch (match) {
case '\r\n':
return '\\r\\n';
case '\r':
return '\\r';
case '\n':
return '\\n';
default:
return match;
}
});
const denormalizeTestNameFromKey = (key: string): string =>
key.replaceAll(/\\r\\n|\\r|\\n/g, match => {
switch (match) {
case '\\r\\n':
return '\r\n';
case '\\r':
return '\r';
case '\\n':
return '\n';
default:
return match;
}
});
export const testNameToKey = (testName: string, count: number): string =>
`${normalizeTestNameForKey(testName)} ${count}`;
export const keyToTestName = (key: string): string => {
if (!/ \d+$/.test(key)) {
throw new Error('Snapshot keys must end with a number.');
}
const testNameWithoutCount = key.replace(/ \d+$/, '');
return denormalizeTestNameFromKey(testNameWithoutCount);
};
export const getSnapshotData = (
snapshotPath: string,
update: Config.SnapshotUpdateState,
): {
data: SnapshotData;
dirty: boolean;
} => {
const data = Object.create(null);
let snapshotContents = '';
let dirty = false;
if (fs.existsSync(snapshotPath)) {
try {
snapshotContents = fs.readFileSync(snapshotPath, 'utf8');
// eslint-disable-next-line no-new-func
const populate = new Function('exports', snapshotContents);
populate(data);
} catch {}
}
const validationResult = validateSnapshotVersion(snapshotContents);
const isInvalid = snapshotContents && validationResult;
if (update === 'none' && isInvalid) {
throw validationResult;
}
if ((update === 'all' || update === 'new') && isInvalid) {
dirty = true;
}
return {data, dirty};
};
export const escapeBacktickString = (str: string): string =>
str.replaceAll(/`|\\|\${/g, '\\$&');
const printBacktickString = (str: string): string =>
`\`${escapeBacktickString(str)}\``;
export const ensureDirectoryExists = (filePath: string): void => {
try {
fs.mkdirSync(path.dirname(filePath), {recursive: true});
} catch {}
};
export const normalizeNewlines = (string: string): string =>
string.replaceAll(/\r\n|\r/g, '\n');
export const saveSnapshotFile = (
snapshotData: SnapshotData,
snapshotPath: string,
): void => {
const snapshots = Object.keys(snapshotData)
.sort(naturalCompare)
.map(
key =>
`exports[${printBacktickString(key)}] = ${printBacktickString(
normalizeNewlines(snapshotData[key]),
)};`,
);
ensureDirectoryExists(snapshotPath);
fs.writeFileSync(
snapshotPath,
`${writeSnapshotVersion()}\n\n${snapshots.join('\n\n')}\n`,
);
};

View File

@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "build"
},
"include": ["./src/**/*"],
"exclude": ["./**/__mocks__/**/*", "./**/__tests__/**/*"],
"references": [{"path": "../jest-types"}]
}