 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>
		
			
				
	
	
		
			229 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			229 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import { TAG_ID as $, NS, ATTRS, getTagID } from './html.js';
 | |
| //MIME types
 | |
| const MIME_TYPES = {
 | |
|     TEXT_HTML: 'text/html',
 | |
|     APPLICATION_XML: 'application/xhtml+xml',
 | |
| };
 | |
| //Attributes
 | |
| const DEFINITION_URL_ATTR = 'definitionurl';
 | |
| const ADJUSTED_DEFINITION_URL_ATTR = 'definitionURL';
 | |
| const SVG_ATTRS_ADJUSTMENT_MAP = new Map([
 | |
|     'attributeName',
 | |
|     'attributeType',
 | |
|     'baseFrequency',
 | |
|     'baseProfile',
 | |
|     'calcMode',
 | |
|     'clipPathUnits',
 | |
|     'diffuseConstant',
 | |
|     'edgeMode',
 | |
|     'filterUnits',
 | |
|     'glyphRef',
 | |
|     'gradientTransform',
 | |
|     'gradientUnits',
 | |
|     'kernelMatrix',
 | |
|     'kernelUnitLength',
 | |
|     'keyPoints',
 | |
|     'keySplines',
 | |
|     'keyTimes',
 | |
|     'lengthAdjust',
 | |
|     'limitingConeAngle',
 | |
|     'markerHeight',
 | |
|     'markerUnits',
 | |
|     'markerWidth',
 | |
|     'maskContentUnits',
 | |
|     'maskUnits',
 | |
|     'numOctaves',
 | |
|     'pathLength',
 | |
|     'patternContentUnits',
 | |
|     'patternTransform',
 | |
|     'patternUnits',
 | |
|     'pointsAtX',
 | |
|     'pointsAtY',
 | |
|     'pointsAtZ',
 | |
|     'preserveAlpha',
 | |
|     'preserveAspectRatio',
 | |
|     'primitiveUnits',
 | |
|     'refX',
 | |
|     'refY',
 | |
|     'repeatCount',
 | |
|     'repeatDur',
 | |
|     'requiredExtensions',
 | |
|     'requiredFeatures',
 | |
|     'specularConstant',
 | |
|     'specularExponent',
 | |
|     'spreadMethod',
 | |
|     'startOffset',
 | |
|     'stdDeviation',
 | |
|     'stitchTiles',
 | |
|     'surfaceScale',
 | |
|     'systemLanguage',
 | |
|     'tableValues',
 | |
|     'targetX',
 | |
|     'targetY',
 | |
|     'textLength',
 | |
|     'viewBox',
 | |
|     'viewTarget',
 | |
|     'xChannelSelector',
 | |
|     'yChannelSelector',
 | |
|     'zoomAndPan',
 | |
| ].map((attr) => [attr.toLowerCase(), attr]));
 | |
| const XML_ATTRS_ADJUSTMENT_MAP = new Map([
 | |
|     ['xlink:actuate', { prefix: 'xlink', name: 'actuate', namespace: NS.XLINK }],
 | |
|     ['xlink:arcrole', { prefix: 'xlink', name: 'arcrole', namespace: NS.XLINK }],
 | |
|     ['xlink:href', { prefix: 'xlink', name: 'href', namespace: NS.XLINK }],
 | |
|     ['xlink:role', { prefix: 'xlink', name: 'role', namespace: NS.XLINK }],
 | |
|     ['xlink:show', { prefix: 'xlink', name: 'show', namespace: NS.XLINK }],
 | |
|     ['xlink:title', { prefix: 'xlink', name: 'title', namespace: NS.XLINK }],
 | |
|     ['xlink:type', { prefix: 'xlink', name: 'type', namespace: NS.XLINK }],
 | |
|     ['xml:lang', { prefix: 'xml', name: 'lang', namespace: NS.XML }],
 | |
|     ['xml:space', { prefix: 'xml', name: 'space', namespace: NS.XML }],
 | |
|     ['xmlns', { prefix: '', name: 'xmlns', namespace: NS.XMLNS }],
 | |
|     ['xmlns:xlink', { prefix: 'xmlns', name: 'xlink', namespace: NS.XMLNS }],
 | |
| ]);
 | |
| //SVG tag names adjustment map
 | |
| export const SVG_TAG_NAMES_ADJUSTMENT_MAP = new Map([
 | |
|     'altGlyph',
 | |
|     'altGlyphDef',
 | |
|     'altGlyphItem',
 | |
|     'animateColor',
 | |
|     'animateMotion',
 | |
|     'animateTransform',
 | |
|     'clipPath',
 | |
|     'feBlend',
 | |
|     'feColorMatrix',
 | |
|     'feComponentTransfer',
 | |
|     'feComposite',
 | |
|     'feConvolveMatrix',
 | |
|     'feDiffuseLighting',
 | |
|     'feDisplacementMap',
 | |
|     'feDistantLight',
 | |
|     'feFlood',
 | |
|     'feFuncA',
 | |
|     'feFuncB',
 | |
|     'feFuncG',
 | |
|     'feFuncR',
 | |
|     'feGaussianBlur',
 | |
|     'feImage',
 | |
|     'feMerge',
 | |
|     'feMergeNode',
 | |
|     'feMorphology',
 | |
|     'feOffset',
 | |
|     'fePointLight',
 | |
|     'feSpecularLighting',
 | |
|     'feSpotLight',
 | |
|     'feTile',
 | |
|     'feTurbulence',
 | |
|     'foreignObject',
 | |
|     'glyphRef',
 | |
|     'linearGradient',
 | |
|     'radialGradient',
 | |
|     'textPath',
 | |
| ].map((tn) => [tn.toLowerCase(), tn]));
 | |
| //Tags that causes exit from foreign content
 | |
| const EXITS_FOREIGN_CONTENT = new Set([
 | |
|     $.B,
 | |
|     $.BIG,
 | |
|     $.BLOCKQUOTE,
 | |
|     $.BODY,
 | |
|     $.BR,
 | |
|     $.CENTER,
 | |
|     $.CODE,
 | |
|     $.DD,
 | |
|     $.DIV,
 | |
|     $.DL,
 | |
|     $.DT,
 | |
|     $.EM,
 | |
|     $.EMBED,
 | |
|     $.H1,
 | |
|     $.H2,
 | |
|     $.H3,
 | |
|     $.H4,
 | |
|     $.H5,
 | |
|     $.H6,
 | |
|     $.HEAD,
 | |
|     $.HR,
 | |
|     $.I,
 | |
|     $.IMG,
 | |
|     $.LI,
 | |
|     $.LISTING,
 | |
|     $.MENU,
 | |
|     $.META,
 | |
|     $.NOBR,
 | |
|     $.OL,
 | |
|     $.P,
 | |
|     $.PRE,
 | |
|     $.RUBY,
 | |
|     $.S,
 | |
|     $.SMALL,
 | |
|     $.SPAN,
 | |
|     $.STRONG,
 | |
|     $.STRIKE,
 | |
|     $.SUB,
 | |
|     $.SUP,
 | |
|     $.TABLE,
 | |
|     $.TT,
 | |
|     $.U,
 | |
|     $.UL,
 | |
|     $.VAR,
 | |
| ]);
 | |
| //Check exit from foreign content
 | |
| export function causesExit(startTagToken) {
 | |
|     const tn = startTagToken.tagID;
 | |
|     const isFontWithAttrs = tn === $.FONT &&
 | |
|         startTagToken.attrs.some(({ name }) => name === ATTRS.COLOR || name === ATTRS.SIZE || name === ATTRS.FACE);
 | |
|     return isFontWithAttrs || EXITS_FOREIGN_CONTENT.has(tn);
 | |
| }
 | |
| //Token adjustments
 | |
| export function adjustTokenMathMLAttrs(token) {
 | |
|     for (let i = 0; i < token.attrs.length; i++) {
 | |
|         if (token.attrs[i].name === DEFINITION_URL_ATTR) {
 | |
|             token.attrs[i].name = ADJUSTED_DEFINITION_URL_ATTR;
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| export function adjustTokenSVGAttrs(token) {
 | |
|     for (let i = 0; i < token.attrs.length; i++) {
 | |
|         const adjustedAttrName = SVG_ATTRS_ADJUSTMENT_MAP.get(token.attrs[i].name);
 | |
|         if (adjustedAttrName != null) {
 | |
|             token.attrs[i].name = adjustedAttrName;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| export function adjustTokenXMLAttrs(token) {
 | |
|     for (let i = 0; i < token.attrs.length; i++) {
 | |
|         const adjustedAttrEntry = XML_ATTRS_ADJUSTMENT_MAP.get(token.attrs[i].name);
 | |
|         if (adjustedAttrEntry) {
 | |
|             token.attrs[i].prefix = adjustedAttrEntry.prefix;
 | |
|             token.attrs[i].name = adjustedAttrEntry.name;
 | |
|             token.attrs[i].namespace = adjustedAttrEntry.namespace;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| export function adjustTokenSVGTagName(token) {
 | |
|     const adjustedTagName = SVG_TAG_NAMES_ADJUSTMENT_MAP.get(token.tagName);
 | |
|     if (adjustedTagName != null) {
 | |
|         token.tagName = adjustedTagName;
 | |
|         token.tagID = getTagID(token.tagName);
 | |
|     }
 | |
| }
 | |
| //Integration points
 | |
| function isMathMLTextIntegrationPoint(tn, ns) {
 | |
|     return ns === NS.MATHML && (tn === $.MI || tn === $.MO || tn === $.MN || tn === $.MS || tn === $.MTEXT);
 | |
| }
 | |
| function isHtmlIntegrationPoint(tn, ns, attrs) {
 | |
|     if (ns === NS.MATHML && tn === $.ANNOTATION_XML) {
 | |
|         for (let i = 0; i < attrs.length; i++) {
 | |
|             if (attrs[i].name === ATTRS.ENCODING) {
 | |
|                 const value = attrs[i].value.toLowerCase();
 | |
|                 return value === MIME_TYPES.TEXT_HTML || value === MIME_TYPES.APPLICATION_XML;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return ns === NS.SVG && (tn === $.FOREIGN_OBJECT || tn === $.DESC || tn === $.TITLE);
 | |
| }
 | |
| export function isIntegrationPoint(tn, ns, attrs, foreignNS) {
 | |
|     return (((!foreignNS || foreignNS === NS.HTML) && isHtmlIntegrationPoint(tn, ns, attrs)) ||
 | |
|         ((!foreignNS || foreignNS === NS.MATHML) && isMathMLTextIntegrationPoint(tn, ns)));
 | |
| }
 |