init project
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
ID,Area,Check,Method,Criterion,Status
|
||||
A11Y-001,Page,One h1 and logical heading order,Automated + manual,WCAG 1.3.1,Required
|
||||
A11Y-002,Keyboard,All interactive controls reachable and operable,Manual,WCAG 2.1.1,Required
|
||||
A11Y-003,Focus,Visible focus and logical order,Manual,"WCAG 2.4.7, 2.4.11",Required
|
||||
A11Y-004,Skip link,Moves focus/viewport to main content,Manual,WCAG 2.4.1,Required
|
||||
A11Y-005,Drawer/dialog,"Containment, inert background, Escape, return focus",Manual + browser,"WCAG 2.4.3, 3.2.1",Required
|
||||
A11Y-006,Forms,"Visible labels, programmatic names, instructions",Automated + SR,"WCAG 1.3.1, 3.3.2",Required
|
||||
A11Y-007,Errors,"Summary, inline association, first invalid focus",SR + manual,"WCAG 3.3.1, 3.3.3",Required
|
||||
A11Y-008,Status,Success/failure announced without focus theft,SR,WCAG 4.1.3,Required
|
||||
A11Y-009,Contrast,"Text, UI, focus in both themes",Automated + manual,"WCAG 1.4.3, 1.4.11",Required
|
||||
A11Y-010,Reflow,320px and 400% zoom,Manual,WCAG 1.4.10,Required
|
||||
A11Y-011,Text spacing,User overrides do not clip,Bookmarklet/manual,WCAG 1.4.12,Required
|
||||
A11Y-012,Motion,Reduced-motion honored,Manual,WCAG 2.3.3,Required
|
||||
A11Y-013,Language,Page and language changes identified,DOM + SR,"WCAG 3.1.1, 3.1.2",Required
|
||||
A11Y-014,Bidi,Mixed-direction values remain intelligible,Visual + SR,WCAG 1.3.2,Required
|
||||
A11Y-015,Targets,Target spacing and size tested,Automated + manual,WCAG 2.5.8,Required
|
||||
A11Y-016,Autofill,Correct autocomplete tokens,DOM + browser,WCAG 1.3.5,Required
|
||||
|
@@ -0,0 +1,10 @@
|
||||
Browser_or_AT,Version_policy,Platform,Coverage
|
||||
Chrome desktop,Latest stable and previous major,"Windows 11, macOS","All functional, visual, performance smoke"
|
||||
Edge desktop,Latest stable and previous major,Windows 11,All functional and visual smoke
|
||||
Firefox desktop,Latest stable and previous major,"Windows 11, macOS","Functional, keyboard, layout, forced-colors where supported"
|
||||
Safari desktop,Latest stable and previous major,macOS,"Functional, dialog, storage, typography, visual"
|
||||
Safari iOS,Latest stable and previous major,Representative iPhone compact and large,"Navigation, dialogs, virtual keyboard, form, viewport"
|
||||
Chrome Android,Latest stable and previous major,Representative Android phone,"Navigation, dialogs, virtual keyboard, form, viewport"
|
||||
Assistive technology,NVDA + Firefox/Chrome current,Windows,Critical paths
|
||||
Assistive technology,VoiceOver + Safari current,macOS/iOS,Critical paths
|
||||
Assistive technology,TalkBack + Chrome current,Android,Critical paths
|
||||
|
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"measurement": "75th percentile real-user data where available; otherwise repeatable lab median on defined mobile profile",
|
||||
"core": {
|
||||
"LCP_ms_max": 2500,
|
||||
"INP_ms_max": 200,
|
||||
"CLS_max": 0.1,
|
||||
"TTFB_ms_max": 800
|
||||
},
|
||||
"transfer_gzip": {
|
||||
"initial_javascript_kb_max": 180,
|
||||
"initial_css_kb_max": 60,
|
||||
"initial_html_kb_max": 100,
|
||||
"hero_media_kb_max": 250,
|
||||
"font_total_kb_max": 220
|
||||
},
|
||||
"requests": {
|
||||
"initial_third_party_max": 2,
|
||||
"initial_font_files_max": 2
|
||||
},
|
||||
"rules": [
|
||||
"No marketing tag may block first paint.",
|
||||
"Load analytics after consent and idle where possible.",
|
||||
"Reserve media dimensions to prevent layout shift.",
|
||||
"No autoplay video on initial load.",
|
||||
"Test EN, FR, and AR because font and copy costs differ."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# Browser Acceptance Specification
|
||||
|
||||
Automated browser tests must cover the scenarios in `visual-regression-scenarios_v1.0.json` plus behavioral assertions from `QA_MATRIX_v1.0.csv`.
|
||||
|
||||
Use semantic locators first: role, accessible name, label, heading, and visible text. Add `data-testid` only where locale-independent state cannot be selected reliably. Never use generated class names as test contracts.
|
||||
|
||||
At minimum, assert server HTML attributes, locale switching with preserved hash, root-only detection, theme no-flash state before app hydration, cookie precedence, system live update, drawer focus loop and inert state, dialog focus return, invalid form focus, recoverable failure value retention, idempotent submit, tour progress, reduced motion, and prohibited PII absence from analytics calls.
|
||||
@@ -0,0 +1,27 @@
|
||||
ID,Area,Scenario,Variants,Method,Expected,Gate
|
||||
QA-001,Routing,Each locale route renders server-correct lang/dir/canonical/hreflang,en/fr/ar,Automated HTML assertion,No hydration correction required,Release
|
||||
QA-002,Localization,Key parity and scalar/array type parity,en/fr/ar,CI validator,"Zero missing, empty, or extra production keys",Release
|
||||
QA-003,Localization,Translation overflow,320/390/768/1024/1440; all locales,Visual + manual,"No clipping, ellipsis, fixed-height collision, or horizontal page scroll",Release
|
||||
QA-004,RTL,Full homepage direction,ar light/dark,Visual + DOM,"Logical order, correct start alignment, selective mirroring",Release
|
||||
QA-005,Mixed direction,"Plate, email, URL, phone, date, currency",ar,Screen reader + visual,Atomic value order remains correct,Release
|
||||
QA-006,Theme,Preference persistence and cookie/storage disagreement,light/dark/system,Browser automation,Cookie wins; no flash; system stays system,Release
|
||||
QA-007,Theme,OS theme changes while System selected,system,Browser automation,Resolved theme updates without changing preference,Release
|
||||
QA-008,Navigation,Desktop header collision and keyboard use,1120/1280/1440; all locales,Visual + keyboard,No overlap; visible focus; logical order,Release
|
||||
QA-009,Navigation,Drawer open/close containment,390/1024; ltr/rtl,Keyboard + SR,"Focus trapped, background inert, Escape and focus return work",Release
|
||||
QA-010,Hero,Primary demo and tour actions,all locales/themes,Browser,Correct dialogs open; analytics source accurate,Release
|
||||
QA-011,Demo form,Empty/invalid submission,all locales,Browser + SR,Summary announced; first invalid focused; inline errors associated,Release
|
||||
QA-012,Demo form,Recoverable server failure,all locales,Integration,Values preserved; retry possible; persistent error,Release
|
||||
QA-013,Demo form,Duplicate click/network retry,all locales,Integration,Single lead due to disabled submit and idempotency,Release
|
||||
QA-014,Demo form,Success,all locales,Integration,Accurate localized confirmation; no PII in analytics,Release
|
||||
QA-015,Tour,Four-step keyboard flow,all locales,Browser + SR,Progress announced; back/next/finish correct; focus moves,Release
|
||||
QA-016,FAQ,Toggle by keyboard and screen reader,all locales,Manual + browser,Summary semantics and state announced,Release
|
||||
QA-017,Accessibility,200% zoom and 400% reflow,320-equivalent; all locales,Manual,No two-dimensional scrolling except data regions,Release
|
||||
QA-018,Accessibility,Forced colors,Windows,Manual,"Controls, focus, boundaries, status remain perceivable",Release
|
||||
QA-019,Accessibility,Reduced motion,all,Browser + manual,No smooth scrolling or essential animation,Release
|
||||
QA-020,Accessibility,Touch target size,compact,Automated + manual,44x44 preferred; WCAG minimum not violated,Release
|
||||
QA-021,Evidence,Claims and assets,production content,Content audit,Every claim has approval or is suppressed,Release
|
||||
QA-022,SEO,Metadata and language alternates,indexable routes,Automated,Unique localized metadata; reciprocal alternates; self-canonical,Release
|
||||
QA-023,Performance,Budgets,mobile + desktop; each locale,Lighthouse/WebPageTest/RUM,All budgets met or approved exception,Release
|
||||
QA-024,Security,CSP and bootstrap,production,Header inspection,Nonce/hash accepted; no unsafe inline expansion,Release
|
||||
QA-025,Privacy,Consent and analytics gating,regions in scope,Integration/manual,No non-essential analytics before required consent,Release
|
||||
QA-026,Visual regression,Named screen states,scenario manifest,Automated screenshots,No unapproved diffs above threshold,Release
|
||||
|
@@ -0,0 +1,14 @@
|
||||
Test_ID,Element,Justification
|
||||
site-header,Header root,Only for visual anchor
|
||||
mobile-drawer,Drawer root,State assertion
|
||||
locale-selector,Locale control,State assertion
|
||||
theme-selector,Theme control,State assertion
|
||||
hero-demo,Hero demo CTA,Source attribution
|
||||
hero-tour,Hero tour CTA,Source attribution
|
||||
demo-dialog,Demo dialog,Overlay state
|
||||
demo-form,Demo form,Submission state
|
||||
demo-alert,Form summary,Error assertion
|
||||
tour-dialog,Tour dialog,Overlay state
|
||||
tour-step,Current tour step,Progress assertion
|
||||
pricing-demo,Pricing CTA,Source attribution
|
||||
final-demo,Final CTA,Source attribution
|
||||
|
@@ -0,0 +1,24 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import crypto from 'node:crypto';
|
||||
const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..');
|
||||
const required = [
|
||||
'09_DEVELOPER_HANDOFF_v1.0.md','DECISION_LOG_v9.0.md','DEFINITION_OF_DONE_v1.0.md','OPEN_ISSUE_REGISTER_v1.0.csv',
|
||||
'tokens/design-tokens_v1.0.json','tokens/semantic-tokens_v1.0.css','components/COMPONENT_CONTRACTS_v1.0.md',
|
||||
'localization/ROUTE_MANIFEST_v1.0.json','localization/locales/en/homepage.json','localization/locales/fr/homepage.json','localization/locales/ar/homepage.json',
|
||||
'interaction/theme-bootstrap_v1.0.js','qa/QA_MATRIX_v1.0.csv','qa/ACCESSIBILITY_CHECKLIST_v1.0.csv','qa/PERFORMANCE_BUDGETS_v1.0.json'
|
||||
];
|
||||
const errors=[]; let checks=0;
|
||||
for (const file of required){ checks++; if(!fs.existsSync(path.join(root,file))) errors.push(`Missing ${file}`); }
|
||||
const readJson=(f)=>JSON.parse(fs.readFileSync(path.join(root,f),'utf8'));
|
||||
for (const file of ['tokens/design-tokens_v1.0.json','localization/ROUTE_MANIFEST_v1.0.json','qa/PERFORMANCE_BUDGETS_v1.0.json','qa/visual-regression-scenarios_v1.0.json']) { try{readJson(file);checks++;}catch(e){errors.push(`${file}: ${e.message}`);} }
|
||||
function shape(value){ if(Array.isArray(value)) return value.map(shape); if(value && typeof value==='object') return Object.fromEntries(Object.keys(value).sort().map(k=>[k,shape(value[k])])); return typeof value; }
|
||||
const locales=['en','fr','ar'].map(l=>readJson(`localization/locales/${l}/homepage.json`));
|
||||
const base=JSON.stringify(shape(locales[0]));
|
||||
for (let i=1;i<locales.length;i++){ checks++; if(JSON.stringify(shape(locales[i]))!==base) errors.push(`Locale shape mismatch: ${['en','fr','ar'][i]}`); }
|
||||
const routes=readJson('localization/ROUTE_MANIFEST_v1.0.json');
|
||||
for (const route of routes.routes){ for (const locale of routes.locales){ checks++; if(!(locale in route.slugs)) errors.push(`Route ${route.id} missing ${locale}`); } }
|
||||
const issueCsv=fs.readFileSync(path.join(root,'OPEN_ISSUE_REGISTER_v1.0.csv'),'utf8'); checks++; if(/,(Critical|High),[^\n]*,,/.test(issueCsv)) errors.push('Critical/High issue missing ownership data');
|
||||
const css=fs.readFileSync(path.join(root,'tokens/semantic-tokens_v1.0.css'),'utf8'); checks++; if(!css.includes('--color-action-conversion')) errors.push('Conversion semantic token missing');
|
||||
if(errors.length){ console.error(JSON.stringify({status:'FAILED',checks,errors},null,2)); process.exit(1); }
|
||||
console.log(JSON.stringify({status:'PASSED',checks,files:fs.readdirSync(root).length},null,2));
|
||||
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"pixelDiffThreshold": 0.01,
|
||||
"scenarios": [
|
||||
{
|
||||
"id": "VR-001",
|
||||
"route": "/en",
|
||||
"viewport": [
|
||||
1440,
|
||||
1000
|
||||
],
|
||||
"theme": "light",
|
||||
"state": "homepage-top"
|
||||
},
|
||||
{
|
||||
"id": "VR-002",
|
||||
"route": "/fr",
|
||||
"viewport": [
|
||||
1024,
|
||||
900
|
||||
],
|
||||
"theme": "dark",
|
||||
"state": "homepage-top"
|
||||
},
|
||||
{
|
||||
"id": "VR-003",
|
||||
"route": "/ar",
|
||||
"viewport": [
|
||||
390,
|
||||
844
|
||||
],
|
||||
"theme": "light",
|
||||
"state": "homepage-top"
|
||||
},
|
||||
{
|
||||
"id": "VR-004",
|
||||
"route": "/ar",
|
||||
"viewport": [
|
||||
390,
|
||||
844
|
||||
],
|
||||
"theme": "dark",
|
||||
"state": "drawer-open"
|
||||
},
|
||||
{
|
||||
"id": "VR-005",
|
||||
"route": "/en",
|
||||
"viewport": [
|
||||
390,
|
||||
844
|
||||
],
|
||||
"theme": "light",
|
||||
"state": "demo-invalid"
|
||||
},
|
||||
{
|
||||
"id": "VR-006",
|
||||
"route": "/fr",
|
||||
"viewport": [
|
||||
768,
|
||||
1024
|
||||
],
|
||||
"theme": "dark",
|
||||
"state": "tour-step-3"
|
||||
},
|
||||
{
|
||||
"id": "VR-007",
|
||||
"route": "/ar",
|
||||
"viewport": [
|
||||
320,
|
||||
800
|
||||
],
|
||||
"theme": "dark",
|
||||
"state": "reflow-gate"
|
||||
},
|
||||
{
|
||||
"id": "VR-008",
|
||||
"route": "/en",
|
||||
"viewport": [
|
||||
1440,
|
||||
1000
|
||||
],
|
||||
"theme": "dark",
|
||||
"state": "pricing-and-final-cta"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user