 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>
		
			
				
	
	
		
			277 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			277 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*!
 | |
|  * /**
 | |
|  *  * 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["default"] = jestHoist;
 | |
| function _template() {
 | |
|   const data = require("@babel/template");
 | |
|   _template = function () {
 | |
|     return data;
 | |
|   };
 | |
|   return data;
 | |
| }
 | |
| function _types() {
 | |
|   const data = require("@babel/types");
 | |
|   _types = 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.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| const JEST_GLOBAL_NAME = 'jest';
 | |
| const JEST_GLOBALS_MODULE_NAME = '@jest/globals';
 | |
| const JEST_GLOBALS_MODULE_JEST_EXPORT_NAME = 'jest';
 | |
| const hoistedVariables = new WeakSet();
 | |
| const hoistedJestGetters = new WeakSet();
 | |
| const hoistedJestExpressions = new WeakSet();
 | |
| 
 | |
| // We allow `jest`, `expect`, `require`, all default Node.js globals and all
 | |
| // ES2015 built-ins to be used inside of a `jest.mock` factory.
 | |
| // We also allow variables prefixed with `mock` as an escape-hatch.
 | |
| const ALLOWED_IDENTIFIERS = new Set(['Array', 'ArrayBuffer', 'Boolean', 'BigInt', 'DataView', 'Date', 'Error', 'EvalError', 'Float32Array', 'Float64Array', 'Function', 'Generator', 'GeneratorFunction', 'Infinity', 'Int16Array', 'Int32Array', 'Int8Array', 'InternalError', 'Intl', 'JSON', 'Map', 'Math', 'NaN', 'Number', 'Object', 'Promise', 'Proxy', 'RangeError', 'ReferenceError', 'Reflect', 'RegExp', 'Set', 'String', 'Symbol', 'SyntaxError', 'TypeError', 'URIError', 'Uint16Array', 'Uint32Array', 'Uint8Array', 'Uint8ClampedArray', 'WeakMap', 'WeakSet', 'arguments', 'console', 'expect', 'isNaN', 'jest', 'parseFloat', 'parseInt', 'exports', 'require', 'module', '__filename', '__dirname', 'undefined', ...Object.getOwnPropertyNames(globalThis)].sort());
 | |
| const IDVisitor = {
 | |
|   ReferencedIdentifier(path, {
 | |
|     ids
 | |
|   }) {
 | |
|     ids.add(path);
 | |
|   },
 | |
|   denylist: ['TypeAnnotation', 'TSTypeAnnotation', 'TSTypeQuery', 'TSTypeReference']
 | |
| };
 | |
| const FUNCTIONS = Object.create(null);
 | |
| FUNCTIONS.mock = args => {
 | |
|   if (args.length === 1) {
 | |
|     return args[0].isStringLiteral() || args[0].isLiteral();
 | |
|   } else if (args.length === 2 || args.length === 3) {
 | |
|     const moduleFactory = args[1];
 | |
|     if (!moduleFactory.isFunction()) {
 | |
|       throw moduleFactory.buildCodeFrameError('The second argument of `jest.mock` must be an inline function.\n', TypeError);
 | |
|     }
 | |
|     const ids = new Set();
 | |
|     const parentScope = moduleFactory.parentPath.scope;
 | |
|     // @ts-expect-error: ReferencedIdentifier and denylist are not known on visitors
 | |
|     moduleFactory.traverse(IDVisitor, {
 | |
|       ids
 | |
|     });
 | |
|     for (const id of ids) {
 | |
|       const {
 | |
|         name
 | |
|       } = id.node;
 | |
|       let found = false;
 | |
|       let scope = id.scope;
 | |
|       while (scope !== parentScope) {
 | |
|         if (scope.bindings[name] != null) {
 | |
|           found = true;
 | |
|           break;
 | |
|         }
 | |
|         scope = scope.parent;
 | |
|       }
 | |
|       if (!found) {
 | |
|         let isAllowedIdentifier = scope.hasGlobal(name) && ALLOWED_IDENTIFIERS.has(name) || /^mock/i.test(name) ||
 | |
|         // Allow istanbul's coverage variable to pass.
 | |
|         /^(?:__)?cov/.test(name);
 | |
|         if (!isAllowedIdentifier) {
 | |
|           const binding = scope.bindings[name];
 | |
|           if (binding?.path.isVariableDeclarator()) {
 | |
|             const {
 | |
|               node
 | |
|             } = binding.path;
 | |
|             const initNode = node.init;
 | |
|             if (initNode && binding.constant && scope.isPure(initNode, true)) {
 | |
|               hoistedVariables.add(node);
 | |
|               isAllowedIdentifier = true;
 | |
|             }
 | |
|           } else if (binding?.path.isImportSpecifier()) {
 | |
|             const importDecl = binding.path.parentPath;
 | |
|             const imported = binding.path.node.imported;
 | |
|             if (importDecl.node.source.value === JEST_GLOBALS_MODULE_NAME && ((0, _types().isIdentifier)(imported) ? imported.name : imported.value) === JEST_GLOBALS_MODULE_JEST_EXPORT_NAME) {
 | |
|               isAllowedIdentifier = true;
 | |
|               // Imports are already hoisted, so we don't need to add it
 | |
|               // to hoistedVariables.
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         if (!isAllowedIdentifier) {
 | |
|           throw id.buildCodeFrameError('The module factory of `jest.mock()` is not allowed to ' + 'reference any out-of-scope variables.\n' + `Invalid variable access: ${name}\n` + `Allowed objects: ${[...ALLOWED_IDENTIFIERS].join(', ')}.\n` + 'Note: This is a precaution to guard against uninitialized mock ' + 'variables. If it is ensured that the mock is required lazily, ' + 'variable names prefixed with `mock` (case insensitive) are permitted.\n', ReferenceError);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| };
 | |
| FUNCTIONS.unmock = args => args.length === 1 && args[0].isStringLiteral();
 | |
| FUNCTIONS.deepUnmock = args => args.length === 1 && args[0].isStringLiteral();
 | |
| FUNCTIONS.disableAutomock = FUNCTIONS.enableAutomock = args => args.length === 0;
 | |
| const createJestObjectGetter = (0, _template().statement)`
 | |
| function GETTER_NAME() {
 | |
|   const { JEST_GLOBALS_MODULE_JEST_EXPORT_NAME } = require("JEST_GLOBALS_MODULE_NAME");
 | |
|   GETTER_NAME = () => JEST_GLOBALS_MODULE_JEST_EXPORT_NAME;
 | |
|   return JEST_GLOBALS_MODULE_JEST_EXPORT_NAME;
 | |
| }
 | |
| `;
 | |
| const isJestObject = expression => {
 | |
|   // global
 | |
|   if (expression.isIdentifier() && expression.node.name === JEST_GLOBAL_NAME && !expression.scope.hasBinding(JEST_GLOBAL_NAME)) {
 | |
|     return true;
 | |
|   }
 | |
|   // import { jest } from '@jest/globals'
 | |
|   if (expression.referencesImport(JEST_GLOBALS_MODULE_NAME, JEST_GLOBALS_MODULE_JEST_EXPORT_NAME)) {
 | |
|     return true;
 | |
|   }
 | |
|   // import * as JestGlobals from '@jest/globals'
 | |
|   if (expression.isMemberExpression() && !expression.node.computed && expression.get('object').referencesImport(JEST_GLOBALS_MODULE_NAME, '*') && expression.node.property.type === 'Identifier' && expression.node.property.name === JEST_GLOBALS_MODULE_JEST_EXPORT_NAME) {
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| };
 | |
| const extractJestObjExprIfHoistable = expr => {
 | |
|   if (!expr.isCallExpression()) {
 | |
|     return null;
 | |
|   }
 | |
|   const callee = expr.get('callee');
 | |
|   const args = expr.get('arguments');
 | |
|   if (!callee.isMemberExpression() || callee.node.computed) {
 | |
|     return null;
 | |
|   }
 | |
|   const object = callee.get('object');
 | |
|   const property = callee.get('property');
 | |
|   const propertyName = property.node.name;
 | |
|   const jestObjExpr = isJestObject(object) ? object :
 | |
|   // The Jest object could be returned from another call since the functions are all chainable.
 | |
|   extractJestObjExprIfHoistable(object)?.path;
 | |
|   if (!jestObjExpr) {
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   // Important: Call the function check last
 | |
|   // It might throw an error to display to the user,
 | |
|   // which should only happen if we're already sure it's a call on the Jest object.
 | |
|   const functionIsHoistable = FUNCTIONS[propertyName]?.(args) ?? false;
 | |
|   let functionHasHoistableScope = functionIsHoistable;
 | |
|   for (let path = expr; path && !functionHasHoistableScope; path = path.parentPath) {
 | |
|     functionHasHoistableScope = hoistedJestExpressions.has(
 | |
|     // @ts-expect-error: it's ok if path.node is not an Expression, .has will
 | |
|     // just return false.
 | |
|     path.node);
 | |
|   }
 | |
|   if (functionHasHoistableScope) {
 | |
|     hoistedJestExpressions.add(expr.node);
 | |
|     return {
 | |
|       hoist: functionIsHoistable,
 | |
|       path: jestObjExpr
 | |
|     };
 | |
|   }
 | |
|   return null;
 | |
| };
 | |
| 
 | |
| /* eslint-disable sort-keys */
 | |
| function jestHoist() {
 | |
|   return {
 | |
|     pre({
 | |
|       path: program
 | |
|     }) {
 | |
|       this.declareJestObjGetterIdentifier = () => {
 | |
|         if (this.jestObjGetterIdentifier) {
 | |
|           return this.jestObjGetterIdentifier;
 | |
|         }
 | |
|         this.jestObjGetterIdentifier = program.scope.generateUidIdentifier('getJestObj');
 | |
|         program.unshiftContainer('body', [createJestObjectGetter({
 | |
|           GETTER_NAME: this.jestObjGetterIdentifier.name,
 | |
|           JEST_GLOBALS_MODULE_JEST_EXPORT_NAME,
 | |
|           JEST_GLOBALS_MODULE_NAME
 | |
|         })]);
 | |
|         return this.jestObjGetterIdentifier;
 | |
|       };
 | |
|     },
 | |
|     visitor: {
 | |
|       ExpressionStatement(exprStmt) {
 | |
|         const jestObjInfo = extractJestObjExprIfHoistable(exprStmt.get('expression'));
 | |
|         if (jestObjInfo) {
 | |
|           const jestCallExpr = (0, _types().callExpression)(this.declareJestObjGetterIdentifier(), []);
 | |
|           jestObjInfo.path.replaceWith(jestCallExpr);
 | |
|           if (jestObjInfo.hoist) {
 | |
|             hoistedJestGetters.add(jestCallExpr);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|     // in `post` to make sure we come after an import transform and can unshift above the `require`s
 | |
|     post({
 | |
|       path: program
 | |
|     }) {
 | |
|       const stack = [{
 | |
|         calls: [],
 | |
|         vars: []
 | |
|       }];
 | |
|       program.traverse({
 | |
|         BlockStatement: {
 | |
|           enter() {
 | |
|             stack.push({
 | |
|               calls: [],
 | |
|               vars: []
 | |
|             });
 | |
|           },
 | |
|           exit(path) {
 | |
|             const item = stack.pop();
 | |
|             path.node.body.unshift(...item.vars, ...item.calls);
 | |
|           }
 | |
|         },
 | |
|         CallExpression(callExpr) {
 | |
|           if (hoistedJestGetters.has(callExpr.node)) {
 | |
|             const mockStmt = callExpr.getStatementParent();
 | |
|             if (mockStmt?.parentPath.isBlock()) {
 | |
|               stack.at(-1).calls.push(mockStmt.node);
 | |
|               mockStmt.remove();
 | |
|             }
 | |
|           }
 | |
|         },
 | |
|         VariableDeclarator(varDecl) {
 | |
|           if (hoistedVariables.has(varDecl.node)) {
 | |
|             // should be assert function, but it's not. So let's cast below
 | |
|             varDecl.parentPath.assertVariableDeclaration();
 | |
|             const {
 | |
|               kind,
 | |
|               declarations
 | |
|             } = varDecl.parent;
 | |
|             if (declarations.length === 1) {
 | |
|               varDecl.parentPath.remove();
 | |
|             } else {
 | |
|               varDecl.remove();
 | |
|             }
 | |
|             stack.at(-1).vars.push((0, _types().variableDeclaration)(kind, [varDecl.node]));
 | |
|           }
 | |
|         }
 | |
|       });
 | |
|       const item = stack.pop();
 | |
|       program.node.body.unshift(...item.vars, ...item.calls);
 | |
|     }
 | |
|   };
 | |
| }
 | |
| /* eslint-enable */
 | |
| })();
 | |
| 
 | |
| module.exports = __webpack_exports__;
 | |
| /******/ })()
 | |
| ; |