From 72cb2819d4ebf0bea9fcf256c7ee89c4f97a0513 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 06:24:39 +0000 Subject: [PATCH] CodeRabbit Generated Unit Tests: Add generated unit tests --- .../accessibility/__tests__/skill.test.mjs | 636 +++++++++++++++ .../animate-text/__tests__/assets.test.mjs | 745 ++++++++++++++++++ 2 files changed, 1381 insertions(+) create mode 100644 .agents/skills/frontend-platform/accessibility/__tests__/skill.test.mjs create mode 100644 .agents/skills/frontend-platform/animate-text/__tests__/assets.test.mjs diff --git a/.agents/skills/frontend-platform/accessibility/__tests__/skill.test.mjs b/.agents/skills/frontend-platform/accessibility/__tests__/skill.test.mjs new file mode 100644 index 00000000..8992719c --- /dev/null +++ b/.agents/skills/frontend-platform/accessibility/__tests__/skill.test.mjs @@ -0,0 +1,636 @@ +/** + * Tests for the accessibility skill files: + * - SKILL.md: YAML frontmatter, POUR principles, conformance levels, section coverage + * - references/A11Y-PATTERNS.md: section headings, required patterns + * - references/WCAG.md: WCAG 2.2 criterion tables, levels, testing tools + * + * Uses Node.js built-in test runner (node:test). Requires Node 18+. + * Run: node --test .agents/skills/frontend-platform/accessibility/__tests__/skill.test.mjs + */ +import { describe, it } from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const SKILL_DIR = join(__dirname, '..'); +const REFS_DIR = join(SKILL_DIR, 'references'); + +function readFile(filePath) { + return readFileSync(filePath, 'utf8'); +} + +// ─── SKILL.md ────────────────────────────────────────────────────────────── + +describe('SKILL.md - file exists and is readable', () => { + it('SKILL.md can be read', () => { + assert.doesNotThrow(() => readFile(join(SKILL_DIR, 'SKILL.md'))); + }); +}); + +describe('SKILL.md - YAML frontmatter', () => { + const content = readFile(join(SKILL_DIR, 'SKILL.md')); + + it('starts with YAML frontmatter delimiter "---"', () => { + assert.ok(content.trimStart().startsWith('---'), 'SKILL.md should begin with YAML frontmatter "---"'); + }); + + it('has a closing frontmatter delimiter', () => { + const lines = content.split('\n'); + const closingDelimiterIndex = lines.slice(1).findIndex((l) => l.trim() === '---'); + assert.ok(closingDelimiterIndex !== -1, 'SKILL.md should have a closing "---" frontmatter delimiter'); + }); + + it('frontmatter contains name field', () => { + assert.ok(content.includes('name: accessibility'), 'SKILL.md frontmatter should include "name: accessibility"'); + }); + + it('frontmatter contains a description field', () => { + assert.match(content, /description:/, 'SKILL.md frontmatter should include a "description" field'); + }); + + it('frontmatter contains license field', () => { + assert.match(content, /license:/, 'SKILL.md frontmatter should include a "license" field'); + }); + + it('frontmatter contains metadata.author field', () => { + assert.match(content, /author:/, 'SKILL.md frontmatter should include an "author" under metadata'); + }); + + it('frontmatter contains metadata.version field', () => { + assert.match(content, /version:/, 'SKILL.md frontmatter should include a "version" under metadata'); + }); + + it('license is MIT', () => { + assert.ok(content.includes('license: MIT'), 'SKILL.md license should be MIT'); + }); +}); + +describe('SKILL.md - WCAG POUR principles section', () => { + const content = readFile(join(SKILL_DIR, 'SKILL.md')); + + it('contains POUR section heading', () => { + assert.match(content, /## WCAG Principles.*POUR/i, 'SKILL.md should have a POUR principles section'); + }); + + it('mentions Perceivable principle', () => { + assert.ok(content.includes('Perceivable'), 'SKILL.md should mention the Perceivable principle'); + }); + + it('mentions Operable principle', () => { + assert.ok(content.includes('Operable'), 'SKILL.md should mention the Operable principle'); + }); + + it('mentions Understandable principle', () => { + assert.ok(content.includes('Understandable'), 'SKILL.md should mention the Understandable principle'); + }); + + it('mentions Robust principle', () => { + assert.ok(content.includes('Robust'), 'SKILL.md should mention the Robust principle'); + }); +}); + +describe('SKILL.md - conformance levels', () => { + const content = readFile(join(SKILL_DIR, 'SKILL.md')); + + it('has conformance levels section', () => { + assert.match(content, /## Conformance levels/i, 'SKILL.md should have a Conformance levels section'); + }); + + it('mentions Level A', () => { + assert.match(content, /\*\*A\*\*/, 'SKILL.md should document Level A'); + }); + + it('mentions Level AA', () => { + assert.match(content, /\*\*AA\*\*/, 'SKILL.md should document Level AA'); + }); + + it('mentions Level AAA', () => { + assert.match(content, /\*\*AAA\*\*/, 'SKILL.md should document Level AAA'); + }); +}); + +describe('SKILL.md - main accessibility topic sections', () => { + const content = readFile(join(SKILL_DIR, 'SKILL.md')); + + it('has Perceivable section', () => { + assert.match(content, /^## Perceivable/m, 'SKILL.md should have a "## Perceivable" section'); + }); + + it('has Operable section', () => { + assert.match(content, /^## Operable/m, 'SKILL.md should have a "## Operable" section'); + }); + + it('has Understandable section', () => { + assert.match(content, /^## Understandable/m, 'SKILL.md should have a "## Understandable" section'); + }); + + it('has Robust section', () => { + assert.match(content, /^## Robust/m, 'SKILL.md should have a "## Robust" section'); + }); + + it('has Testing checklist section', () => { + assert.match(content, /## Testing checklist/i, 'SKILL.md should have a Testing checklist section'); + }); +}); + +describe('SKILL.md - color contrast ratios documented', () => { + const content = readFile(join(SKILL_DIR, 'SKILL.md')); + + it('documents 4.5:1 contrast ratio for normal text (AA)', () => { + assert.ok(content.includes('4.5:1'), 'SKILL.md should document the 4.5:1 color contrast ratio'); + }); + + it('documents 3:1 contrast ratio for large text (AA)', () => { + assert.ok(content.includes('3:1'), 'SKILL.md should document the 3:1 color contrast ratio'); + }); + + it('documents 7:1 contrast ratio for enhanced (AAA)', () => { + assert.ok(content.includes('7:1'), 'SKILL.md should document the 7:1 enhanced contrast ratio'); + }); +}); + +describe('SKILL.md - WCAG 2.2 new criteria mentioned', () => { + const content = readFile(join(SKILL_DIR, 'SKILL.md')); + + it('documents Focus Not Obscured (2.4.11)', () => { + assert.match(content, /2\.4\.11|Focus Not Obscured/i, 'SKILL.md should reference WCAG 2.4.11 Focus Not Obscured'); + }); + + it('documents Target Size (2.5.8)', () => { + assert.match(content, /2\.5\.8|Target Size/i, 'SKILL.md should reference WCAG 2.5.8 Target Size'); + }); + + it('documents Dragging Movements (2.5.7)', () => { + assert.match(content, /2\.5\.7|[Dd]ragging/i, 'SKILL.md should reference WCAG 2.5.7 Dragging Movements'); + }); + + it('documents Redundant Entry (3.3.7)', () => { + assert.match(content, /3\.3\.7|[Rr]edundant [Ee]ntry/i, 'SKILL.md should reference WCAG 3.3.7 Redundant Entry'); + }); + + it('documents Accessible Authentication (3.3.8)', () => { + assert.match(content, /3\.3\.8|[Aa]ccessible [Aa]uthentication/i, 'SKILL.md should reference WCAG 3.3.8 Accessible Authentication'); + }); + + it('documents Consistent Help (3.2.6)', () => { + assert.match(content, /3\.2\.6|[Cc]onsistent [Hh]elp/i, 'SKILL.md should reference WCAG 3.2.6 Consistent Help'); + }); +}); + +describe('SKILL.md - visually-hidden CSS class documented', () => { + const content = readFile(join(SKILL_DIR, 'SKILL.md')); + + it('documents .visually-hidden CSS class', () => { + assert.ok(content.includes('visually-hidden'), 'SKILL.md should document the visually-hidden CSS pattern'); + }); + + it('visually-hidden uses position: absolute', () => { + assert.ok( + content.includes('position: absolute') || content.includes('position:absolute'), + 'SKILL.md visually-hidden pattern should include position: absolute' + ); + }); +}); + +describe('SKILL.md - prefers-reduced-motion documented', () => { + const content = readFile(join(SKILL_DIR, 'SKILL.md')); + + it('references prefers-reduced-motion media query', () => { + assert.ok( + content.includes('prefers-reduced-motion'), + 'SKILL.md should document the prefers-reduced-motion media query' + ); + }); +}); + +describe('SKILL.md - focus-visible documented', () => { + const content = readFile(join(SKILL_DIR, 'SKILL.md')); + + it('references :focus-visible CSS pseudo-class', () => { + assert.ok(content.includes(':focus-visible'), 'SKILL.md should reference :focus-visible pseudo-class'); + }); +}); + +describe('SKILL.md - references to companion files', () => { + const content = readFile(join(SKILL_DIR, 'SKILL.md')); + + it('links to A11Y-PATTERNS.md', () => { + assert.ok( + content.includes('A11Y-PATTERNS.md'), + 'SKILL.md should reference the A11Y-PATTERNS.md companion file' + ); + }); + + it('links to WCAG.md', () => { + assert.ok( + content.includes('WCAG.md'), + 'SKILL.md should reference the WCAG.md companion file' + ); + }); +}); + +describe('SKILL.md - target size minimum is 24x24 CSS pixels', () => { + const content = readFile(join(SKILL_DIR, 'SKILL.md')); + + it('documents 24x24 minimum target size', () => { + assert.ok( + content.includes('24') && content.includes('24px'), + 'SKILL.md should document the 24x24 CSS pixel minimum target size' + ); + }); +}); + +// ─── references/A11Y-PATTERNS.md ─────────────────────────────────────────── + +describe('A11Y-PATTERNS.md - file exists and is readable', () => { + it('A11Y-PATTERNS.md can be read', () => { + assert.doesNotThrow(() => readFile(join(REFS_DIR, 'A11Y-PATTERNS.md'))); + }); +}); + +describe('A11Y-PATTERNS.md - required pattern sections', () => { + const content = readFile(join(REFS_DIR, 'A11Y-PATTERNS.md')); + + it('has modal focus trap section', () => { + assert.match(content, /## Modal focus trap/i, 'A11Y-PATTERNS.md should have a Modal focus trap section'); + }); + + it('has skip link section', () => { + assert.match(content, /## Skip link/i, 'A11Y-PATTERNS.md should have a Skip link section'); + }); + + it('has error handling section', () => { + assert.match(content, /## Error handling/i, 'A11Y-PATTERNS.md should have an Error handling section'); + }); + + it('has form labels section', () => { + assert.match(content, /## Form labels/i, 'A11Y-PATTERNS.md should have a Form labels section'); + }); + + it('has dragging movements section', () => { + assert.match(content, /## Dragging movements/i, 'A11Y-PATTERNS.md should have a Dragging movements section'); + }); + + it('has ARIA tabs section', () => { + assert.match(content, /## ARIA tabs/i, 'A11Y-PATTERNS.md should have an ARIA tabs section'); + }); + + it('has live regions section', () => { + assert.match(content, /## Live regions/i, 'A11Y-PATTERNS.md should have a Live regions section'); + }); + + it('has screen reader commands section', () => { + assert.match(content, /## Screen reader commands/i, 'A11Y-PATTERNS.md should have a Screen reader commands section'); + }); +}); + +describe('A11Y-PATTERNS.md - modal focus trap code', () => { + const content = readFile(join(REFS_DIR, 'A11Y-PATTERNS.md')); + + it('contains Tab key handling', () => { + assert.ok(content.includes("'Tab'") || content.includes('"Tab"'), 'Focus trap should handle the Tab key'); + }); + + it('contains Escape key handling', () => { + assert.ok(content.includes("'Escape'") || content.includes('"Escape"'), 'Focus trap should handle the Escape key'); + }); + + it('contains focusable elements selector', () => { + assert.ok(content.includes('button'), 'Focus trap should list focusable element types'); + }); +}); + +describe('A11Y-PATTERNS.md - skip link pattern', () => { + const content = readFile(join(REFS_DIR, 'A11Y-PATTERNS.md')); + + it('skip link uses href="#main-content"', () => { + assert.ok( + content.includes('#main-content'), + 'Skip link pattern should reference "#main-content" anchor' + ); + }); + + it('skip link CSS moves it visually off-screen by default', () => { + assert.ok( + content.includes('top: -40px') || content.includes('top:-40px'), + 'Skip link should be positioned off-screen by default (top: -40px)' + ); + }); + + it('skip link CSS shows it on focus', () => { + assert.ok( + content.includes('.skip-link:focus') || content.includes('skip-link:focus'), + 'Skip link should be visible on :focus' + ); + }); +}); + +describe('A11Y-PATTERNS.md - error handling pattern', () => { + const content = readFile(join(REFS_DIR, 'A11Y-PATTERNS.md')); + + it('uses aria-invalid attribute', () => { + assert.ok(content.includes('aria-invalid'), 'Error handling should use aria-invalid'); + }); + + it('uses aria-describedby for error messages', () => { + assert.ok(content.includes('aria-describedby'), 'Error handling should use aria-describedby'); + }); + + it('uses role="alert" for error messages', () => { + assert.ok(content.includes('role="alert"'), 'Error handling should use role="alert"'); + }); + + it('focuses first error on submit', () => { + assert.ok( + content.includes('firstError') || content.includes('focus()'), + 'Error handling should focus the first error on form submit' + ); + }); +}); + +describe('A11Y-PATTERNS.md - form labels pattern', () => { + const content = readFile(join(REFS_DIR, 'A11Y-PATTERNS.md')); + + it('shows explicit label using for/id association', () => { + assert.ok( + content.includes('for="') && content.includes('id="'), + 'Form labels should demonstrate explicit for/id association' + ); + }); + + it('shows implicit label wrapping input', () => { + assert.match(content, /