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:
7
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/cloneEvent.js
generated
vendored
Normal file
7
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/cloneEvent.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
function cloneEvent(event) {
|
||||
return new event.constructor(event.type, event);
|
||||
}
|
||||
|
||||
exports.cloneEvent = cloneEvent;
|
||||
14
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/findClosest.js
generated
vendored
Normal file
14
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/findClosest.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
function findClosest(element, callback) {
|
||||
let el = element;
|
||||
do {
|
||||
if (callback(el)) {
|
||||
return el;
|
||||
}
|
||||
el = el.parentElement;
|
||||
}while (el && el !== element.ownerDocument.body)
|
||||
return undefined;
|
||||
}
|
||||
|
||||
exports.findClosest = findClosest;
|
||||
10
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/getDocumentFromNode.js
generated
vendored
Normal file
10
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/getDocumentFromNode.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
function getDocumentFromNode(el) {
|
||||
return isDocument(el) ? el : el.ownerDocument;
|
||||
}
|
||||
function isDocument(node) {
|
||||
return node.nodeType === 9;
|
||||
}
|
||||
|
||||
exports.getDocumentFromNode = getDocumentFromNode;
|
||||
25
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/getTreeDiff.js
generated
vendored
Normal file
25
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/getTreeDiff.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
function getTreeDiff(a, b) {
|
||||
const treeA = [];
|
||||
for(let el = a; el; el = el.parentElement){
|
||||
treeA.push(el);
|
||||
}
|
||||
const treeB = [];
|
||||
for(let el = b; el; el = el.parentElement){
|
||||
treeB.push(el);
|
||||
}
|
||||
let i = 0;
|
||||
for(;; i++){
|
||||
if (i >= treeA.length || i >= treeB.length || treeA[treeA.length - 1 - i] !== treeB[treeB.length - 1 - i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [
|
||||
treeA.slice(0, treeA.length - i),
|
||||
treeB.slice(0, treeB.length - i),
|
||||
treeB.slice(treeB.length - i)
|
||||
];
|
||||
}
|
||||
|
||||
exports.getTreeDiff = getTreeDiff;
|
||||
19
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/getWindow.js
generated
vendored
Normal file
19
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/getWindow.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
function getWindow(node) {
|
||||
var _node_ownerDocument;
|
||||
if (isDocument(node) && node.defaultView) {
|
||||
return node.defaultView;
|
||||
} else if ((_node_ownerDocument = node.ownerDocument) === null || _node_ownerDocument === undefined ? undefined : _node_ownerDocument.defaultView) {
|
||||
return node.ownerDocument.defaultView;
|
||||
}
|
||||
throw new Error(`Could not determine window of node. Node was ${describe(node)}`);
|
||||
}
|
||||
function isDocument(node) {
|
||||
return node.nodeType === 9;
|
||||
}
|
||||
function describe(val) {
|
||||
return typeof val === 'function' ? `function ${val.name}` : val === null ? 'null' : String(val);
|
||||
}
|
||||
|
||||
exports.getWindow = getWindow;
|
||||
14
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/isDescendantOrSelf.js
generated
vendored
Normal file
14
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/isDescendantOrSelf.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
function isDescendantOrSelf(potentialDescendant, potentialAncestor) {
|
||||
let el = potentialDescendant;
|
||||
do {
|
||||
if (el === potentialAncestor) {
|
||||
return true;
|
||||
}
|
||||
el = el.parentElement;
|
||||
}while (el)
|
||||
return false;
|
||||
}
|
||||
|
||||
exports.isDescendantOrSelf = isDescendantOrSelf;
|
||||
33
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/isDisabled.js
generated
vendored
Normal file
33
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/isDisabled.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
var isElementType = require('./isElementType.js');
|
||||
|
||||
// This should probably just rely on the :disabled pseudo-class, but JSDOM doesn't implement it properly.
|
||||
function isDisabled(element) {
|
||||
for(let el = element; el; el = el.parentElement){
|
||||
if (isElementType.isElementType(el, [
|
||||
'button',
|
||||
'input',
|
||||
'select',
|
||||
'textarea',
|
||||
'optgroup',
|
||||
'option'
|
||||
])) {
|
||||
if (el.hasAttribute('disabled')) {
|
||||
return true;
|
||||
}
|
||||
} else if (isElementType.isElementType(el, 'fieldset')) {
|
||||
var _el_querySelector;
|
||||
if (el.hasAttribute('disabled') && !((_el_querySelector = el.querySelector(':scope > legend')) === null || _el_querySelector === undefined ? undefined : _el_querySelector.contains(element))) {
|
||||
return true;
|
||||
}
|
||||
} else if (el.tagName.includes('-')) {
|
||||
if (el.constructor.formAssociated && el.hasAttribute('disabled')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
exports.isDisabled = isDisabled;
|
||||
20
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/isElementType.js
generated
vendored
Normal file
20
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/isElementType.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
function isElementType(element, tag, props) {
|
||||
if (element.namespaceURI && element.namespaceURI !== 'http://www.w3.org/1999/xhtml') {
|
||||
return false;
|
||||
}
|
||||
tag = Array.isArray(tag) ? tag : [
|
||||
tag
|
||||
];
|
||||
// tagName is uppercase in HTMLDocument and lowercase in XMLDocument
|
||||
if (!tag.includes(element.tagName.toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
if (props) {
|
||||
return Object.entries(props).every(([k, v])=>element[k] === v);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
exports.isElementType = isElementType;
|
||||
19
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/isVisible.js
generated
vendored
Normal file
19
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/isVisible.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
var getWindow = require('./getWindow.js');
|
||||
|
||||
function isVisible(element) {
|
||||
const window = getWindow.getWindow(element);
|
||||
for(let el = element; el === null || el === undefined ? undefined : el.ownerDocument; el = el.parentElement){
|
||||
const { display, visibility } = window.getComputedStyle(el);
|
||||
if (display === 'none') {
|
||||
return false;
|
||||
}
|
||||
if (visibility === 'hidden') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
exports.isVisible = isVisible;
|
||||
17
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/level.js
generated
vendored
Normal file
17
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/level.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
var ApiLevel = /*#__PURE__*/ function(ApiLevel) {
|
||||
ApiLevel[ApiLevel["Trigger"] = 2] = "Trigger";
|
||||
ApiLevel[ApiLevel["Call"] = 1] = "Call";
|
||||
return ApiLevel;
|
||||
}({});
|
||||
function setLevelRef(instance, level) {
|
||||
instance.levelRefs[level] = {};
|
||||
}
|
||||
function getLevelRef(instance, level) {
|
||||
return instance.levelRefs[level];
|
||||
}
|
||||
|
||||
exports.ApiLevel = ApiLevel;
|
||||
exports.getLevelRef = getLevelRef;
|
||||
exports.setLevelRef = setLevelRef;
|
||||
14
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/wait.js
generated
vendored
Normal file
14
frontend/node_modules/@testing-library/user-event/dist/cjs/utils/misc/wait.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
function wait(config) {
|
||||
const delay = config.delay;
|
||||
if (typeof delay !== 'number') {
|
||||
return;
|
||||
}
|
||||
return Promise.all([
|
||||
new Promise((resolve)=>globalThis.setTimeout(()=>resolve(), delay)),
|
||||
config.advanceTimers(delay)
|
||||
]);
|
||||
}
|
||||
|
||||
exports.wait = wait;
|
||||
Reference in New Issue
Block a user