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

353
frontend/node_modules/jest-worker/build/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,353 @@
/**
* 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 {ForkOptions} from 'child_process';
import {ResourceLimits} from 'worker_threads';
declare const CHILD_MESSAGE_CALL = 1;
declare const CHILD_MESSAGE_CALL_SETUP = 4;
declare const CHILD_MESSAGE_END = 2;
declare const CHILD_MESSAGE_INITIALIZE = 0;
declare const CHILD_MESSAGE_MEM_USAGE = 3;
declare type ChildMessage =
| ChildMessageInitialize
| ChildMessageCall
| ChildMessageEnd
| ChildMessageMemUsage
| ChildMessageCallSetup;
declare type ChildMessageCall = [
type: typeof CHILD_MESSAGE_CALL,
isProcessed: boolean,
methodName: string,
args: Array<unknown>,
];
declare type ChildMessageCallSetup = [type: typeof CHILD_MESSAGE_CALL_SETUP];
declare type ChildMessageEnd = [
type: typeof CHILD_MESSAGE_END,
isProcessed: boolean,
];
declare type ChildMessageInitialize = [
type: typeof CHILD_MESSAGE_INITIALIZE,
isProcessed: boolean,
fileName: string,
setupArgs: Array<unknown>,
workerId: string | undefined,
];
declare type ChildMessageMemUsage = [type: typeof CHILD_MESSAGE_MEM_USAGE];
declare type ComputeTaskPriorityCallback = (
method: string,
...args: Array<unknown>
) => number;
declare type ExcludeReservedKeys<K> = Exclude<K, ReservedKeys>;
/**
* First-in, First-out task queue that manages a dedicated pool
* for each worker as well as a shared queue. The FIFO ordering is guaranteed
* across the worker specific and shared queue.
*/
export declare class FifoQueue implements TaskQueue {
private _workerQueues;
private readonly _sharedQueue;
enqueue(task: QueueChildMessage, workerId?: number): void;
dequeue(workerId: number): QueueChildMessage | null;
}
declare type FunctionLike = (...args: any) => unknown;
declare type HeapItem = {
priority: number;
};
export declare type JestWorkerFarm<T extends Record<string, unknown>> =
Worker_2 & WorkerModule<T>;
export declare function messageParent(
message: unknown,
parentProcess?: NodeJS.Process,
): void;
declare type MethodLikeKeys<T> = {
[K in keyof T]: T[K] extends FunctionLike ? K : never;
}[keyof T];
declare class MinHeap<TItem extends HeapItem> {
private readonly _heap;
peek(): TItem | null;
add(item: TItem): void;
poll(): TItem | null;
}
declare type OnCustomMessage = (message: Array<unknown> | unknown) => void;
declare type OnEnd = (err: Error | null, result: unknown) => void;
declare type OnStart = (worker: WorkerInterface) => void;
declare type OnStateChangeHandler = (
state: WorkerStates,
oldState: WorkerStates,
) => void;
declare type PoolExitResult = {
forceExited: boolean;
};
/**
* Priority queue that processes tasks in natural ordering (lower priority first)
* according to the priority computed by the function passed in the constructor.
*
* FIFO ordering isn't guaranteed for tasks with the same priority.
*
* Worker specific tasks with the same priority as a non-worker specific task
* are always processed first.
*/
export declare class PriorityQueue implements TaskQueue {
private readonly _computePriority;
private _queue;
private readonly _sharedQueue;
constructor(_computePriority: ComputeTaskPriorityCallback);
enqueue(task: QueueChildMessage, workerId?: number): void;
_enqueue(task: QueueChildMessage, queue: MinHeap<QueueItem>): void;
dequeue(workerId: number): QueueChildMessage | null;
_getWorkerQueue(workerId: number): MinHeap<QueueItem>;
}
export declare interface PromiseWithCustomMessage<T> extends Promise<T> {
UNSTABLE_onCustomMessage?: (listener: OnCustomMessage) => () => void;
}
declare type Promisify<T extends FunctionLike> =
ReturnType<T> extends Promise<infer R>
? (...args: Parameters<T>) => Promise<R>
: (...args: Parameters<T>) => Promise<ReturnType<T>>;
declare type QueueChildMessage = {
request: ChildMessageCall;
onStart: OnStart;
onEnd: OnEnd;
onCustomMessage: OnCustomMessage;
};
declare type QueueItem = {
task: QueueChildMessage;
priority: number;
};
declare type ReservedKeys =
| 'end'
| 'getStderr'
| 'getStdout'
| 'setup'
| 'teardown';
export declare interface TaskQueue {
/**
* Enqueues the task in the queue for the specified worker or adds it to the
* queue shared by all workers
* @param task the task to queue
* @param workerId the id of the worker that should process this task or undefined
* if there's no preference.
*/
enqueue(task: QueueChildMessage, workerId?: number): void;
/**
* Dequeues the next item from the queue for the specified worker
* @param workerId the id of the worker for which the next task should be retrieved
*/
dequeue(workerId: number): QueueChildMessage | null;
}
/**
* The Jest farm (publicly called "Worker") is a class that allows you to queue
* methods across multiple child processes, in order to parallelize work. This
* is done by providing an absolute path to a module that will be loaded on each
* of the child processes, and bridged to the main process.
*
* Bridged methods are specified by using the "exposedMethods" property of the
* "options" object. This is an array of strings, where each of them corresponds
* to the exported name in the loaded module.
*
* You can also control the amount of workers by using the "numWorkers" property
* of the "options" object, and the settings passed to fork the process through
* the "forkOptions" property. The amount of workers defaults to the amount of
* CPUS minus one.
*
* Queueing calls can be done in two ways:
* - Standard method: calls will be redirected to the first available worker,
* so they will get executed as soon as they can.
*
* - Sticky method: if a "computeWorkerKey" method is provided within the
* config, the resulting string of this method will be used as a key.
* Every time this key is returned, it is guaranteed that your job will be
* processed by the same worker. This is specially useful if your workers
* are caching results.
*/
declare class Worker_2 {
private _ending;
private readonly _farm;
private readonly _options;
private readonly _workerPool;
constructor(workerPath: string | URL, options?: WorkerFarmOptions);
private _bindExposedWorkerMethods;
private _callFunctionWithArgs;
getStderr(): NodeJS.ReadableStream;
getStdout(): NodeJS.ReadableStream;
start(): Promise<void>;
end(): Promise<PoolExitResult>;
}
export {Worker_2 as Worker};
declare type WorkerCallback = (
workerId: number,
request: ChildMessage,
onStart: OnStart,
onEnd: OnEnd,
onCustomMessage: OnCustomMessage,
) => void;
declare enum WorkerEvents {
STATE_CHANGE = 'state-change',
}
export declare type WorkerFarmOptions = {
computeWorkerKey?: (method: string, ...args: Array<unknown>) => string | null;
enableWorkerThreads?: boolean;
exposedMethods?: ReadonlyArray<string>;
forkOptions?: ForkOptions;
maxRetries?: number;
numWorkers?: number;
resourceLimits?: ResourceLimits;
setupArgs?: Array<unknown>;
taskQueue?: TaskQueue;
WorkerPool?: new (
workerPath: string,
options?: WorkerPoolOptions,
) => WorkerPoolInterface;
workerSchedulingPolicy?: WorkerSchedulingPolicy;
idleMemoryLimit?: number;
};
declare interface WorkerInterface {
get state(): WorkerStates;
send(
request: ChildMessage,
onProcessStart: OnStart,
onProcessEnd: OnEnd,
onCustomMessage: OnCustomMessage,
): void;
waitForExit(): Promise<void>;
forceExit(): void;
getWorkerId(): number;
getStderr(): NodeJS.ReadableStream | null;
getStdout(): NodeJS.ReadableStream | null;
/**
* Some system level identifier for the worker. IE, process id, thread id, etc.
*/
getWorkerSystemId(): number;
getMemoryUsage(): Promise<number | null>;
/**
* Checks to see if the child worker is actually running.
*/
isWorkerRunning(): boolean;
/**
* When the worker child is started and ready to start handling requests.
*
* @remarks
* This mostly exists to help with testing so that you don't check the status
* of things like isWorkerRunning before it actually is.
*/
waitForWorkerReady(): Promise<void>;
}
declare type WorkerModule<T> = {
[K in keyof T as Extract<
ExcludeReservedKeys<K>,
MethodLikeKeys<T>
>]: T[K] extends FunctionLike ? Promisify<T[K]> : never;
};
declare type WorkerOptions_2 = {
forkOptions: ForkOptions;
resourceLimits: ResourceLimits;
setupArgs: Array<unknown>;
maxRetries: number;
workerId: number;
workerData?: unknown;
workerPath: string;
/**
* After a job has executed the memory usage it should return to.
*
* @remarks
* Note this is different from ResourceLimits in that it checks at idle, after
* a job is complete. So you could have a resource limit of 500MB but an idle
* limit of 50MB. The latter will only trigger if after a job has completed the
* memory usage hasn't returned back down under 50MB.
*/
idleMemoryLimit?: number;
/**
* This mainly exists so the path can be changed during testing.
* https://github.com/jestjs/jest/issues/9543
*/
childWorkerPath?: string;
/**
* This is useful for debugging individual tests allowing you to see
* the raw output of the worker.
*/
silent?: boolean;
/**
* Used to immediately bind event handlers.
*/
on?: {
[WorkerEvents.STATE_CHANGE]:
| OnStateChangeHandler
| ReadonlyArray<OnStateChangeHandler>;
};
};
export declare interface WorkerPoolInterface {
getStderr(): NodeJS.ReadableStream;
getStdout(): NodeJS.ReadableStream;
getWorkers(): Array<WorkerInterface>;
createWorker(options: WorkerOptions_2): WorkerInterface;
send: WorkerCallback;
start(): Promise<void>;
end(): Promise<PoolExitResult>;
}
export declare type WorkerPoolOptions = {
setupArgs: Array<unknown>;
forkOptions: ForkOptions;
resourceLimits: ResourceLimits;
maxRetries: number;
numWorkers: number;
enableWorkerThreads: boolean;
idleMemoryLimit?: number;
};
declare type WorkerSchedulingPolicy = 'round-robin' | 'in-order';
declare enum WorkerStates {
STARTING = 'starting',
OK = 'ok',
OUT_OF_MEMORY = 'oom',
RESTARTING = 'restarting',
SHUTTING_DOWN = 'shutting-down',
SHUT_DOWN = 'shut-down',
}
export {};

1902
frontend/node_modules/jest-worker/build/index.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

6
frontend/node_modules/jest-worker/build/index.mjs generated vendored Normal file
View File

@@ -0,0 +1,6 @@
import cjsModule from './index.js';
export const FifoQueue = cjsModule.FifoQueue;
export const PriorityQueue = cjsModule.PriorityQueue;
export const Worker = cjsModule.Worker;
export const messageParent = cjsModule.messageParent;

310
frontend/node_modules/jest-worker/build/processChild.js generated vendored Normal file
View File

@@ -0,0 +1,310 @@
/*!
* /**
* * 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/types.ts":
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports.WorkerStates = exports.WorkerEvents = exports.PARENT_MESSAGE_SETUP_ERROR = exports.PARENT_MESSAGE_OK = exports.PARENT_MESSAGE_MEM_USAGE = exports.PARENT_MESSAGE_CUSTOM = exports.PARENT_MESSAGE_CLIENT_ERROR = exports.CHILD_MESSAGE_MEM_USAGE = exports.CHILD_MESSAGE_INITIALIZE = exports.CHILD_MESSAGE_END = exports.CHILD_MESSAGE_CALL_SETUP = exports.CHILD_MESSAGE_CALL = void 0;
/**
* 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.
*/
// Because of the dynamic nature of a worker communication process, all messages
// coming from any of the other processes cannot be typed. Thus, many types
// include "unknown" as a TS type, which is (unfortunately) correct here.
const CHILD_MESSAGE_INITIALIZE = exports.CHILD_MESSAGE_INITIALIZE = 0;
const CHILD_MESSAGE_CALL = exports.CHILD_MESSAGE_CALL = 1;
const CHILD_MESSAGE_END = exports.CHILD_MESSAGE_END = 2;
const CHILD_MESSAGE_MEM_USAGE = exports.CHILD_MESSAGE_MEM_USAGE = 3;
const CHILD_MESSAGE_CALL_SETUP = exports.CHILD_MESSAGE_CALL_SETUP = 4;
const PARENT_MESSAGE_OK = exports.PARENT_MESSAGE_OK = 0;
const PARENT_MESSAGE_CLIENT_ERROR = exports.PARENT_MESSAGE_CLIENT_ERROR = 1;
const PARENT_MESSAGE_SETUP_ERROR = exports.PARENT_MESSAGE_SETUP_ERROR = 2;
const PARENT_MESSAGE_CUSTOM = exports.PARENT_MESSAGE_CUSTOM = 3;
const PARENT_MESSAGE_MEM_USAGE = exports.PARENT_MESSAGE_MEM_USAGE = 4;
// Option objects.
// Messages passed from the parent to the children.
// Messages passed from the children to the parent.
// Queue types.
let WorkerStates = exports.WorkerStates = /*#__PURE__*/function (WorkerStates) {
WorkerStates["STARTING"] = "starting";
WorkerStates["OK"] = "ok";
WorkerStates["OUT_OF_MEMORY"] = "oom";
WorkerStates["RESTARTING"] = "restarting";
WorkerStates["SHUTTING_DOWN"] = "shutting-down";
WorkerStates["SHUT_DOWN"] = "shut-down";
return WorkerStates;
}({});
let WorkerEvents = exports.WorkerEvents = /*#__PURE__*/function (WorkerEvents) {
WorkerEvents["STATE_CHANGE"] = "state-change";
return WorkerEvents;
}({});
/***/ }),
/***/ "./src/workers/safeMessageTransferring.ts":
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports.packMessage = packMessage;
exports.unpackMessage = unpackMessage;
function _structuredClone() {
const data = require("@ungap/structured-clone");
_structuredClone = function () {
return data;
};
return data;
}
/**
* 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.
*/
function packMessage(message) {
return {
__STRUCTURED_CLONE_SERIALIZED__: true,
/**
* Use the `json: true` option to avoid errors
* caused by `function` or `symbol` types.
* It's not ideal to lose `function` and `symbol` types,
* but reliability is more important.
*/
data: (0, _structuredClone().serialize)(message, {
json: true
})
};
}
function isTransferringContainer(message) {
return message != null && typeof message === 'object' && '__STRUCTURED_CLONE_SERIALIZED__' in message && 'data' in message;
}
function unpackMessage(message) {
if (isTransferringContainer(message)) {
return (0, _structuredClone().deserialize)(message.data);
}
return message;
}
/***/ })
/******/ });
/************************************************************************/
/******/ // 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;
/******/ }
/******/
/************************************************************************/
var __webpack_exports__ = {};
function _nodeUtil() {
const data = require("node:util");
_nodeUtil = function () {
return data;
};
return data;
}
function _jestUtil() {
const data = require("jest-util");
_jestUtil = function () {
return data;
};
return data;
}
var _types = __webpack_require__("./src/types.ts");
var _safeMessageTransferring = __webpack_require__("./src/workers/safeMessageTransferring.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.
*/
let file = null;
let setupArgs = [];
let initialized = false;
/**
* This file is a small bootstrapper for workers. It sets up the communication
* between the worker and the parent process, interpreting parent messages and
* sending results back.
*
* The file loaded will be lazily initialized the first time any of the workers
* is called. This is done for optimal performance: if the farm is initialized,
* but no call is made to it, child Node processes will be consuming the least
* possible amount of memory.
*
* If an invalid message is detected, the child will exit (by throwing) with a
* non-zero exit code.
*/
const messageListener = request => {
switch (request[0]) {
case _types.CHILD_MESSAGE_INITIALIZE:
const init = request;
file = init[2];
setupArgs = init[3];
break;
case _types.CHILD_MESSAGE_CALL:
const call = request;
execMethod(call[2], call[3]);
break;
case _types.CHILD_MESSAGE_END:
end();
break;
case _types.CHILD_MESSAGE_MEM_USAGE:
reportMemoryUsage();
break;
case _types.CHILD_MESSAGE_CALL_SETUP:
if (initialized) {
reportSuccess(void 0);
} else {
const main = require(file);
initialized = true;
if (main.setup) {
execFunction(main.setup, main, setupArgs, reportSuccess, reportInitializeError);
} else {
reportSuccess(void 0);
}
}
break;
default:
throw new TypeError(`Unexpected request from parent process: ${request[0]}`);
}
};
process.on('message', messageListener);
function reportSuccess(result) {
if (!process || !process.send) {
throw new Error('Child can only be used on a forked process');
}
try {
process.send([_types.PARENT_MESSAGE_OK, result]);
} catch (error) {
if (_nodeUtil().types.isNativeError(error) &&
// if .send is a function, it's a serialization issue
!error.message.includes('.send is not a function')) {
// Apply specific serialization only in error cases
// to avoid affecting performance in regular cases.
process.send([_types.PARENT_MESSAGE_OK, (0, _safeMessageTransferring.packMessage)(result)]);
} else {
throw error;
}
}
}
function reportClientError(error) {
return reportError(error, _types.PARENT_MESSAGE_CLIENT_ERROR);
}
function reportInitializeError(error) {
return reportError(error, _types.PARENT_MESSAGE_SETUP_ERROR);
}
function reportMemoryUsage() {
if (!process || !process.send) {
throw new Error('Child can only be used on a forked process');
}
const msg = [_types.PARENT_MESSAGE_MEM_USAGE, process.memoryUsage().heapUsed];
process.send(msg);
}
function reportError(error, type) {
if (!process || !process.send) {
throw new Error('Child can only be used on a forked process');
}
if (error == null) {
error = new Error('"null" or "undefined" thrown');
}
process.send([type, error.constructor && error.constructor.name, error.message, error.stack, typeof error === 'object' ? {
...error
} : error]);
}
function end() {
const main = require(file);
if (!main.teardown) {
exitProcess();
return;
}
execFunction(main.teardown, main, [], exitProcess, exitProcess);
}
function exitProcess() {
// Clean up open handles so the process ideally exits gracefully
process.removeListener('message', messageListener);
}
function execMethod(method, args) {
const main = require(file);
let fn;
if (method === 'default') {
fn = main.__esModule ? main.default : main;
} else {
fn = main[method];
}
function execHelper() {
execFunction(fn, main, args, reportSuccess, reportClientError);
}
if (initialized || !main.setup) {
execHelper();
return;
}
initialized = true;
execFunction(main.setup, main, setupArgs, execHelper, reportInitializeError);
}
function execFunction(fn, ctx, args, onResult, onError) {
let result;
try {
result = fn.apply(ctx, args);
} catch (error) {
onError(error);
return;
}
if ((0, _jestUtil().isPromise)(result)) {
result.then(onResult, onError);
} else {
onResult(result);
}
}
module.exports = __webpack_exports__;
/******/ })()
;

347
frontend/node_modules/jest-worker/build/threadChild.js generated vendored Normal file
View File

@@ -0,0 +1,347 @@
/*!
* /**
* * 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/types.ts":
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports.WorkerStates = exports.WorkerEvents = exports.PARENT_MESSAGE_SETUP_ERROR = exports.PARENT_MESSAGE_OK = exports.PARENT_MESSAGE_MEM_USAGE = exports.PARENT_MESSAGE_CUSTOM = exports.PARENT_MESSAGE_CLIENT_ERROR = exports.CHILD_MESSAGE_MEM_USAGE = exports.CHILD_MESSAGE_INITIALIZE = exports.CHILD_MESSAGE_END = exports.CHILD_MESSAGE_CALL_SETUP = exports.CHILD_MESSAGE_CALL = void 0;
/**
* 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.
*/
// Because of the dynamic nature of a worker communication process, all messages
// coming from any of the other processes cannot be typed. Thus, many types
// include "unknown" as a TS type, which is (unfortunately) correct here.
const CHILD_MESSAGE_INITIALIZE = exports.CHILD_MESSAGE_INITIALIZE = 0;
const CHILD_MESSAGE_CALL = exports.CHILD_MESSAGE_CALL = 1;
const CHILD_MESSAGE_END = exports.CHILD_MESSAGE_END = 2;
const CHILD_MESSAGE_MEM_USAGE = exports.CHILD_MESSAGE_MEM_USAGE = 3;
const CHILD_MESSAGE_CALL_SETUP = exports.CHILD_MESSAGE_CALL_SETUP = 4;
const PARENT_MESSAGE_OK = exports.PARENT_MESSAGE_OK = 0;
const PARENT_MESSAGE_CLIENT_ERROR = exports.PARENT_MESSAGE_CLIENT_ERROR = 1;
const PARENT_MESSAGE_SETUP_ERROR = exports.PARENT_MESSAGE_SETUP_ERROR = 2;
const PARENT_MESSAGE_CUSTOM = exports.PARENT_MESSAGE_CUSTOM = 3;
const PARENT_MESSAGE_MEM_USAGE = exports.PARENT_MESSAGE_MEM_USAGE = 4;
// Option objects.
// Messages passed from the parent to the children.
// Messages passed from the children to the parent.
// Queue types.
let WorkerStates = exports.WorkerStates = /*#__PURE__*/function (WorkerStates) {
WorkerStates["STARTING"] = "starting";
WorkerStates["OK"] = "ok";
WorkerStates["OUT_OF_MEMORY"] = "oom";
WorkerStates["RESTARTING"] = "restarting";
WorkerStates["SHUTTING_DOWN"] = "shutting-down";
WorkerStates["SHUT_DOWN"] = "shut-down";
return WorkerStates;
}({});
let WorkerEvents = exports.WorkerEvents = /*#__PURE__*/function (WorkerEvents) {
WorkerEvents["STATE_CHANGE"] = "state-change";
return WorkerEvents;
}({});
/***/ }),
/***/ "./src/workers/isDataCloneError.ts":
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports.isDataCloneError = isDataCloneError;
/**
* 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.
*/
// https://webidl.spec.whatwg.org/#datacloneerror
const DATA_CLONE_ERROR_CODE = 25;
/**
* Unfortunately, [`util.types.isNativeError(value)`](https://nodejs.org/api/util.html#utiltypesisnativeerrorvalue)
* return `false` for `DataCloneError` error.
* For this reason, try to detect it in this way
*/
function isDataCloneError(error) {
return error != null && typeof error === 'object' && 'name' in error && error.name === 'DataCloneError' && 'message' in error && typeof error.message === 'string' && 'code' in error && error.code === DATA_CLONE_ERROR_CODE;
}
/***/ }),
/***/ "./src/workers/safeMessageTransferring.ts":
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports.packMessage = packMessage;
exports.unpackMessage = unpackMessage;
function _structuredClone() {
const data = require("@ungap/structured-clone");
_structuredClone = function () {
return data;
};
return data;
}
/**
* 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.
*/
function packMessage(message) {
return {
__STRUCTURED_CLONE_SERIALIZED__: true,
/**
* Use the `json: true` option to avoid errors
* caused by `function` or `symbol` types.
* It's not ideal to lose `function` and `symbol` types,
* but reliability is more important.
*/
data: (0, _structuredClone().serialize)(message, {
json: true
})
};
}
function isTransferringContainer(message) {
return message != null && typeof message === 'object' && '__STRUCTURED_CLONE_SERIALIZED__' in message && 'data' in message;
}
function unpackMessage(message) {
if (isTransferringContainer(message)) {
return (0, _structuredClone().deserialize)(message.data);
}
return message;
}
/***/ })
/******/ });
/************************************************************************/
/******/ // 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;
/******/ }
/******/
/************************************************************************/
var __webpack_exports__ = {};
function _worker_threads() {
const data = require("worker_threads");
_worker_threads = function () {
return data;
};
return data;
}
function _jestUtil() {
const data = require("jest-util");
_jestUtil = function () {
return data;
};
return data;
}
var _types = __webpack_require__("./src/types.ts");
var _isDataCloneError = __webpack_require__("./src/workers/isDataCloneError.ts");
var _safeMessageTransferring = __webpack_require__("./src/workers/safeMessageTransferring.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.
*/
let file = null;
let setupArgs = [];
let initialized = false;
/**
* This file is a small bootstrapper for workers. It sets up the communication
* between the worker and the parent process, interpreting parent messages and
* sending results back.
*
* The file loaded will be lazily initialized the first time any of the workers
* is called. This is done for optimal performance: if the farm is initialized,
* but no call is made to it, child Node processes will be consuming the least
* possible amount of memory.
*
* If an invalid message is detected, the child will exit (by throwing) with a
* non-zero exit code.
*/
const messageListener = request => {
switch (request[0]) {
case _types.CHILD_MESSAGE_INITIALIZE:
const init = request;
file = init[2];
setupArgs = init[3];
process.env.JEST_WORKER_ID = init[4];
break;
case _types.CHILD_MESSAGE_CALL:
const call = request;
execMethod(call[2], call[3]);
break;
case _types.CHILD_MESSAGE_END:
end();
break;
case _types.CHILD_MESSAGE_MEM_USAGE:
reportMemoryUsage();
break;
case _types.CHILD_MESSAGE_CALL_SETUP:
if (initialized) {
reportSuccess(void 0);
} else {
const main = require(file);
initialized = true;
if (main.setup) {
execFunction(main.setup, main, setupArgs, reportSuccess, reportInitializeError);
} else {
reportSuccess(void 0);
}
}
break;
default:
throw new TypeError(`Unexpected request from parent process: ${request[0]}`);
}
};
_worker_threads().parentPort.on('message', messageListener);
function reportMemoryUsage() {
if (_worker_threads().isMainThread) {
throw new Error('Child can only be used on a forked process');
}
const msg = [_types.PARENT_MESSAGE_MEM_USAGE, process.memoryUsage().heapUsed];
_worker_threads().parentPort.postMessage(msg);
}
function reportSuccess(result) {
if (_worker_threads().isMainThread) {
throw new Error('Child can only be used on a forked process');
}
try {
_worker_threads().parentPort.postMessage([_types.PARENT_MESSAGE_OK, result]);
} catch (error) {
let resolvedError = error;
// Try to handle https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
// for `symbols` and `functions`
if ((0, _isDataCloneError.isDataCloneError)(error)) {
try {
_worker_threads().parentPort.postMessage([_types.PARENT_MESSAGE_OK, (0, _safeMessageTransferring.packMessage)(result)]);
return;
} catch (secondTryError) {
resolvedError = secondTryError;
}
}
// Handling it here to avoid unhandled rejection
// which is hard to distinguish on the parent side
reportClientError(resolvedError);
}
}
function reportClientError(error) {
return reportError(error, _types.PARENT_MESSAGE_CLIENT_ERROR);
}
function reportInitializeError(error) {
return reportError(error, _types.PARENT_MESSAGE_SETUP_ERROR);
}
function reportError(error, type) {
if (_worker_threads().isMainThread) {
throw new Error('Child can only be used on a forked process');
}
if (error == null) {
error = new Error('"null" or "undefined" thrown');
}
_worker_threads().parentPort.postMessage([type, error.constructor && error.constructor.name, error.message, error.stack, typeof error === 'object' ? {
...error
} : error]);
}
function end() {
const main = require(file);
if (!main.teardown) {
exitProcess();
return;
}
execFunction(main.teardown, main, [], exitProcess, exitProcess);
}
function exitProcess() {
// Clean up open handles so the worker ideally exits gracefully
_worker_threads().parentPort.removeListener('message', messageListener);
}
function execMethod(method, args) {
const main = require(file);
let fn;
if (method === 'default') {
fn = main.__esModule ? main.default : main;
} else {
fn = main[method];
}
function execHelper() {
execFunction(fn, main, args, reportSuccess, reportClientError);
}
if (initialized || !main.setup) {
execHelper();
return;
}
initialized = true;
execFunction(main.setup, main, setupArgs, execHelper, reportInitializeError);
}
function execFunction(fn, ctx, args, onResult, onError) {
let result;
try {
result = fn.apply(ctx, args);
} catch (error) {
onError(error);
return;
}
if ((0, _jestUtil().isPromise)(result)) {
result.then(onResult, onError);
} else {
onResult(result);
}
}
module.exports = __webpack_exports__;
/******/ })()
;