From e7d18efb87cb3ed811192007e381b15d4d5a0c83 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Sun, 13 Sep 2026 16:14:36 +0000 Subject: [PATCH] wcag: preserve report dates and correct W3C hand-off --- README.md | 13 ++++++++++--- bin/wcag.ts | 2 +- src/wcag.ts | 17 ++++++++++++----- test/wcag.test.ts | 9 +++++++++ 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9297949..1759f33 100644 --- a/README.md +++ b/README.md @@ -1165,9 +1165,16 @@ pages before a second post), load each in headless Chrome, run [axe-core](https://github.com/dequelabs/axe-core) over it, and print one row per success criterion: how many pages fail it, how many need a look, and the rule with the most elements in violation. `report` turns that into the tool's -own evaluation file, which "Open evaluation" loads with the scope, the sample -and one assertion per page and criterion filled in, so the evaluator starts -at the judgement calls rather than at an empty form. +own evaluation file, which "Open report" on the Overview page loads with the +scope, the sample and one assertion per page and criterion filled in, so the +evaluator starts at the judgement calls rather than at an empty form. + +In "4. Evaluate sample set", select the pages under "Add results for samples" +(or "Select all"), then expand "Show samples to enter individual results" +beneath a criterion to see its imported findings. The "Entire sample set" +outcomes remain "Not checked" for the evaluator to complete, so the tool's +overall progress starts at 0 even when page findings are present. Save with +"View report" > "Download report (JSON)". **Nothing is ever marked passed.** axe can prove a failure — an image with no alternative fails 1.1.1 wherever it is — but "no rule fired" proves nothing diff --git a/bin/wcag.ts b/bin/wcag.ts index dad2e04..bf2f214 100644 --- a/bin/wcag.ts +++ b/bin/wcag.ts @@ -217,7 +217,7 @@ async function runReport(argv: string[]): Promise { const assertions = (evaluation.auditSample as unknown[]).length; const sampled = ((evaluation.selectSample as { structuredSample: unknown[] }).structuredSample).length; - out(`${outFile}: ${sampled} pages in the sample, ${assertions} assertions. Open it at ${REPORT_TOOL_URL} with "Open evaluation".`); + out(`${outFile}: ${sampled} pages in the sample, ${assertions} assertions. Open it at ${REPORT_TOOL_URL} with "Open report" on the Overview page. Run \`wcag open\` for the full steps.`); return 0; } diff --git a/src/wcag.ts b/src/wcag.ts index 1aa95e6..be64312 100644 --- a/src/wcag.ts +++ b/src/wcag.ts @@ -33,7 +33,7 @@ * `reportFindings`, a `Webpage` subject per sampled page whose id is its * URL, and an `Assertion` per page and criterion with `test` set to the * tool's own criterion ids (`WCAG22:non-text-content`). A file of that - * shape goes through the tool's "Open evaluation" rather than the beta + * shape goes through the tool's "Open report" rather than the beta * assertion import, which is the path that restores the sample as well. */ @@ -1363,7 +1363,7 @@ const describe = (outcome: CriterionOutcome, page: PageResult, report: Report): /** * The report tool's own evaluation file, with the sample and the automated - * results filled in. Open it in the tool with "Open evaluation". + * results filled in. Open it in the tool with "Open report". */ export function toEvaluation(report: Report, options: EvaluationOptions = {}): Record { const { wcagVersion: version } = report; @@ -1456,7 +1456,9 @@ export function toEvaluation(report: Report, options: EvaluationOptions = {}): R reportFindings: { documentSteps: [{ '@id': '_:about' }, { '@id': '_:defineScope' }, { '@id': '_:exploreTarget' }, { '@id': '_:selectSample' }], commissioner: options.commissioner ?? '', - date, + // The tool reads reportFindings.date['@value']; an untyped string is lost. + // Use a calendar date for the report and keep precise assertion timestamps. + date: { type: 'http://www.w3.org/TR/NOTE-datetime', '@value': date.slice(0, 10) }, evaluator: options.evaluator ?? '', evaluationSpecifics: '', summary, @@ -1483,11 +1485,16 @@ export const OPEN_STEPS = `The WCAG-EM Report Tool is a web page with no command 1. wcag audit https://example.org --pages 8 writes wcag-report.json 2. wcag report wcag-report.json writes evaluation.json 3. open ${REPORT_TOOL_URL} - 4. "Open evaluation" in the menu, choose evaluation.json + 4. "Open report" on the Overview page, choose evaluation.json + 5. In "4. Evaluate sample set", select the pages under "Add results for samples" + (or "Select all"), then expand "Show samples to enter individual results" + beneath a criterion to see its imported findings. Steps 1 (scope) and 3 (sample) are then filled in, and step 4 (audit) holds one assertion per page and criterion: "failed" where axe proved a failure, "cannot tell" where it found something to look at or only checked a part. Nothing is marked "passed" — that is the evaluator's call, made in the tool. +The "Entire sample set" outcomes stay "Not checked" for the evaluator to finish, +so the report's overall progress remains 0 until those outcomes are recorded. The tool keeps the evaluation in the browser and saves it back out as JSON -from step 5.`; +with "View report" > "Download report (JSON)".`; diff --git a/test/wcag.test.ts b/test/wcag.test.ts index 7bb3929..2aacf2e 100644 --- a/test/wcag.test.ts +++ b/test/wcag.test.ts @@ -432,6 +432,15 @@ describe('toEvaluation', () => { expect(evaluation.reportFindings.evaluator).toBe('Anthony'); }); + it('preserves the evaluation date in the typed value the report tool imports', () => { + // W3C's importer reads reportFindings.date['@value']; a plain string + // silently becomes an empty date. Use YYYY-MM-DD for the report date. + expect(evaluation.reportFindings.date).toEqual({ + type: 'http://www.w3.org/TR/NOTE-datetime', + '@value': '2026-09-13', + }); + }); + // The tool derives a sampled page's id from the URL in its description and // finds it again by title, so both have to be exactly this. it('puts every loaded page in the structured sample with its URL as id and description', () => {