@progress/kendo-e2e
Version:
Kendo UI end-to-end test utilities.
122 lines • 5.13 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateIdReferences = validateIdReferences;
exports.formatIdRefValidation = formatIdRefValidation;
exports.formatIdRefForLLM = formatIdRefForLLM;
const sanitize_html_1 = __importDefault(require("sanitize-html"));
const parse_html_1 = require("./parse-html");
/** Attributes whose values are space-separated lists of ID references */
const ID_REF_ATTRIBUTES = ['aria-controls', 'aria-labelledby', 'aria-owns', 'aria-describedby'];
// ============= Validation =============
/**
* Validate that ID-referencing aria attributes in the actual HTML point to
* elements that actually exist in the same DOM.
*
* Checks `aria-controls`, `aria-labelledby`, `aria-owns`, and `aria-describedby`.
* Each attribute value is treated as a space-separated list of ID tokens
* (per the WAI-ARIA spec).
*
* @param actualHtml The actual HTML markup to validate.
* @returns An `IdRefResult` with `valid` and `invalid` reference lists.
*/
function validateIdReferences(actualHtml) {
const result = { valid: [], invalid: [] };
const config = {
allowedTags: false,
allowVulnerableTags: true,
allowedAttributes: { "*": ["class", "id", "role", "aria-*"] },
};
const doc = (0, parse_html_1.parseHtml)((0, sanitize_html_1.default)(actualHtml, config));
// Collect all IDs in the DOM
const allIds = new Set();
for (const el of doc.querySelectorAll('[id]')) {
const id = el.getAttribute('id');
if (id)
allIds.add(id);
}
// Walk all elements that have ID-referencing attributes
for (const attrName of ID_REF_ATTRIBUTES) {
for (const el of doc.querySelectorAll(`[${attrName}]`)) {
const value = el.getAttribute(attrName);
if (!value)
continue;
const selector = buildClassSelector(el);
const ids = value.split(/\s+/).filter(Boolean);
for (const id of ids) {
if (allIds.has(id)) {
result.valid.push({ selector, attribute: attrName, id });
}
else {
result.invalid.push({ selector, attribute: attrName, missingId: id });
}
}
}
}
return result;
}
/** Build a CSS class selector string from an element (e.g. ".k-button.k-button-md") */
function buildClassSelector(el) {
const classAttr = el.getAttribute('class') || '';
const classes = classAttr.split(/\s+/).filter(Boolean).sort();
return classes.length > 0 ? '.' + classes.join('.') : el.tagName.toLowerCase();
}
// ============= Formatting =============
const helpers_1 = require("./helpers");
/**
* Format ID reference validation results for terminal output.
*/
function formatIdRefValidation(result, options) {
const c = (options === null || options === void 0 ? void 0 : options.useColors) ? helpers_1.ANSI : helpers_1.NO_ANSI;
const sep = '═'.repeat(62);
const thinSep = '─'.repeat(62);
const lines = [];
lines.push('');
lines.push(`${c.dim}${sep}${c.reset}`);
lines.push(`${c.bold} A11Y ID REFERENCE VALIDATION${c.reset}`);
lines.push(`${c.dim}${sep}${c.reset}`);
lines.push('');
lines.push(` ${c.green}✓ ${result.valid.length} valid${c.reset}` +
(result.invalid.length > 0 ? ` ${c.red}✗ ${result.invalid.length} broken${c.reset}` : ''));
if (result.invalid.length === 0) {
lines.push('');
lines.push(` ${c.green}All ID references resolve to existing elements!${c.reset}`);
lines.push(`${c.dim}${sep}${c.reset}`);
return lines.join('\n');
}
lines.push('');
lines.push(`${c.dim}${thinSep}${c.reset}`);
lines.push(`${c.bold}${c.red} BROKEN ID REFERENCES${c.reset}${c.red} (attribute points to non-existent ID):${c.reset}`);
lines.push(`${c.dim}${thinSep}${c.reset}`);
for (const issue of result.invalid) {
lines.push(` ${c.red}✗ ${issue.selector} ${issue.attribute}="${issue.missingId}" → ID not found${c.reset}`);
}
lines.push('');
lines.push(`${c.dim}${sep}${c.reset}`);
return lines.join('\n');
}
/**
* Format ID reference validation as LLM-friendly plain text.
*/
function formatIdRefForLLM(result) {
const thin = '-'.repeat(60);
const lines = [];
lines.push('');
lines.push(thin);
lines.push('ID REFERENCE VALIDATION');
lines.push(thin);
lines.push(`IDREF_STATUS: ${result.invalid.length === 0 ? 'PASS' : 'FAIL'}`);
lines.push(`IDREF_VALID: ${result.valid.length}`);
lines.push(`IDREF_BROKEN: ${result.invalid.length}`);
if (result.invalid.length > 0) {
lines.push('');
lines.push('BROKEN_IDREFS:');
for (const issue of result.invalid) {
lines.push(` ✗ ${issue.selector} ${issue.attribute}="${issue.missingId}" → ID not found`);
}
}
return lines.join('\n');
}
//# sourceMappingURL=a11y-id-validator.js.map