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

View File

@@ -0,0 +1,100 @@
import { PointerEventsCheckLevel } from '../../options.js';
import { getWindow } from '../misc/getWindow.js';
import { isElementType } from '../misc/isElementType.js';
import { ApiLevel, getLevelRef } from '../misc/level.js';
function hasPointerEvents(instance, element) {
var _checkPointerEvents;
return ((_checkPointerEvents = checkPointerEvents(instance, element)) === null || _checkPointerEvents === undefined ? undefined : _checkPointerEvents.pointerEvents) !== 'none';
}
function closestPointerEventsDeclaration(element) {
const window = getWindow(element);
for(let el = element, tree = []; el === null || el === undefined ? undefined : el.ownerDocument; el = el.parentElement){
tree.push(el);
const pointerEvents = window.getComputedStyle(el).pointerEvents;
if (pointerEvents && ![
'inherit',
'unset'
].includes(pointerEvents)) {
return {
pointerEvents,
tree
};
}
}
return undefined;
}
const PointerEventsCheck = Symbol('Last check for pointer-events');
function checkPointerEvents(instance, element) {
const lastCheck = element[PointerEventsCheck];
const needsCheck = instance.config.pointerEventsCheck !== PointerEventsCheckLevel.Never && (!lastCheck || hasBitFlag(instance.config.pointerEventsCheck, PointerEventsCheckLevel.EachApiCall) && lastCheck[ApiLevel.Call] !== getLevelRef(instance, ApiLevel.Call) || hasBitFlag(instance.config.pointerEventsCheck, PointerEventsCheckLevel.EachTrigger) && lastCheck[ApiLevel.Trigger] !== getLevelRef(instance, ApiLevel.Trigger));
if (!needsCheck) {
return lastCheck === null || lastCheck === undefined ? undefined : lastCheck.result;
}
const declaration = closestPointerEventsDeclaration(element);
element[PointerEventsCheck] = {
[ApiLevel.Call]: getLevelRef(instance, ApiLevel.Call),
[ApiLevel.Trigger]: getLevelRef(instance, ApiLevel.Trigger),
result: declaration
};
return declaration;
}
function assertPointerEvents(instance, element) {
const declaration = checkPointerEvents(instance, element);
if ((declaration === null || declaration === undefined ? undefined : declaration.pointerEvents) === 'none') {
throw new Error([
`Unable to perform pointer interaction as the element ${declaration.tree.length > 1 ? 'inherits' : 'has'} \`pointer-events: none\`:`,
'',
printTree(declaration.tree)
].join('\n'));
}
}
function printTree(tree) {
return tree.reverse().map((el, i)=>[
''.padEnd(i),
el.tagName,
el.id && `#${el.id}`,
el.hasAttribute('data-testid') && `(testId=${el.getAttribute('data-testid')})`,
getLabelDescr(el),
tree.length > 1 && i === 0 && ' <-- This element declared `pointer-events: none`',
tree.length > 1 && i === tree.length - 1 && ' <-- Asserted pointer events here'
].filter(Boolean).join('')).join('\n');
}
function getLabelDescr(element) {
var _element_labels;
let label;
if (element.hasAttribute('aria-label')) {
label = element.getAttribute('aria-label');
} else if (element.hasAttribute('aria-labelledby')) {
var _element_ownerDocument_getElementById_textContent, _element_ownerDocument_getElementById;
label = (_element_ownerDocument_getElementById = element.ownerDocument.getElementById(element.getAttribute('aria-labelledby'))) === null || _element_ownerDocument_getElementById === undefined ? undefined : (_element_ownerDocument_getElementById_textContent = _element_ownerDocument_getElementById.textContent) === null || _element_ownerDocument_getElementById_textContent === undefined ? undefined : _element_ownerDocument_getElementById_textContent.trim();
} else if (isElementType(element, [
'button',
'input',
'meter',
'output',
'progress',
'select',
'textarea'
]) && ((_element_labels = element.labels) === null || _element_labels === undefined ? undefined : _element_labels.length)) {
label = Array.from(element.labels).map((el)=>{
var _el_textContent;
return (_el_textContent = el.textContent) === null || _el_textContent === undefined ? undefined : _el_textContent.trim();
}).join('|');
} else if (isElementType(element, 'button')) {
var _element_textContent;
label = (_element_textContent = element.textContent) === null || _element_textContent === undefined ? undefined : _element_textContent.trim();
}
label = label === null || label === undefined ? undefined : label.replace(/\n/g, ' ');
if (Number(label === null || label === undefined ? undefined : label.length) > 30) {
label = `${label === null || label === undefined ? undefined : label.substring(0, 29)}`;
}
return label ? `(label=${label})` : '';
}
// With the eslint rule and prettier the bitwise operation isn't nice to read
function hasBitFlag(conf, flag) {
// eslint-disable-next-line no-bitwise
return (conf & flag) > 0;
}
export { assertPointerEvents, hasPointerEvents };