EARL-Bericht

@surea11y/core/earl gibt Scan-Ergebnisse als EARL 1.0 in JSON-LD aus: das Vokabular, das das W3C veröffentlicht, um festzuhalten „dieses Tool hat diese Sache getestet und dieses Ergebnis bekommen“, und das Format, das die Community Group der ACT Rules als Implementierungsbericht akzeptiert.

const { renderEarlReport } = require('@surea11y/core/earl');
 
const report = renderEarlReport(result, {
assertor: { name: 'surea11y', version: '1.14.1' },
mode: 'earl:automatic'
});
 
fs.writeFileSync('earl.jsonld', JSON.stringify(report, null, 2));

Wofür er gedacht ist

Zwei Zielgruppen, die dasselbe Dokument aus unterschiedlichen Gründen wollen.

Ein Implementierungsbericht zeigt der ACT Rules Community Group, wie sich diese Engine bei ihren Testfällen verhält; darüber wird eine Engine neben den anderen Implementierungen gelistet. Eine Listung ist keine Empfehlung, und das W3C prüft die Daten nicht. Korrekt heißt es „als ACT-Implementierung gelistet“, nie „W3C-zertifiziert“. Der Bericht dieser Engine ist unter https://surea11y.github.io/act-report/act-report.jsonld veröffentlicht und wird von scripts/act-report.js neu erzeugt. --runner=chromium erzeugt denselben Bericht aus einem Scan in Chromium, so wie die Browser-Bindings die Engine ausführen, mit dem Assertor „SureA11y in a browser“ und der Chromium-Version in der Beschreibung des Berichts.

Wer die Engine nutzt, bekommt ein Austauschformat. EARL ist das, was Barrierefreiheits-Tools lesen, wenn sie Ergebnisse aus mehr als einer Quelle zusammenführen müssen, etwa aus einem automatischen Scan und einem manuellen Audit oder aus mehreren Engines, denn jede Aussage hält fest, wer sie getroffen hat und wie. Das lohnt sich unabhängig davon, ob je etwas irgendwo eingereicht wird.

Wie er sich von den anderen Berichten unterscheidet

SARIF-Bericht und HTML-Bericht enthalten beide nur Verstöße: Sie melden fail- und cantTell-Vorkommen und verwerfen alles andere. EARL ist das Gegenteil. Jede ausgeführte Regel wird zu einer Aussage, pass und inapplicable eingeschlossen, weil ein Implementierungsbericht eine Aussage darüber ist, was die Engine überall entschieden hat. Eine Regel, die stumm blieb, weil sie nichts Anwendbares fand, ist ein Beleg, kein Rauschen: Nur so lässt sich unterscheiden zwischen „diese Engine hat geprüft und nichts zu prüfen gefunden“ und „diese Engine implementiert diese Regel gar nicht“. Jede Aussage betrifft eine Regel auf einem Subjekt: Ein passed einer Regel ist kein Urteil über das WCAG-Kriterium, dem sie zugeordnet ist; die meisten Kriterien haben Teile, die keine Regel abdeckt (siehe WCAG-Konformitätszuordnung).

Aufbau

Der Graph ist nach Subjekt gruppiert, statt eine flache Liste von Aussagen zu sein:

{
"@context": "https://www.w3.org/WAI/content-assets/wcag-act-rules/earl-context.json",
"@graph": [
{
"@type": "TestSubject",
"source": "https://example.test/",
"assertions": [
{
"@type": "Assertion",
"test": { "title": "img-alt-present", "isPartOf": ["WCAG2:non-text-content"] },
"result": { "outcome": "earl:failed" },
"assertedBy": {
"@type": "Assertor",
"name": "surea11y",
"release": { "@type": "Version", "revision": "1.14.1" }
},
"mode": "earl:automatic"
}
]
}
]
}
  • source ist die gescannte URL oder about:blank, wenn ein Ergebnis keine enthält. Enthalten mehrere Ergebnisse keine, wird jedes zu einem eigenen Subjekt, about:blank#result-<n> nach seiner Position in der Liste (ab 1), statt dass alle zu einem zusammenfallen.
  • test.title ist die Regel-ID der Engine. In ACT-Begriffen ist eine Regel das Verfahren, das die Implementierung ausgeführt hat, und genau das benennt eine Regel-ID.
  • test.isPartOf listet die Erfolgskriterien, denen die Regel zugeordnet ist, als WCAG2:<criterion-id>: jeden Eintrag in normativeMappings, dessen standard WCAG ist oder fehlt (wie in SARIF) und der keinen type hat. Bei einer Regel, die kein Kriterium beansprucht, fehlt das Feld ganz: aria-allowed-role, empty-heading und landmark-role-name-present sind die automatischen Regeln der Engine in dieser Lage, und eine leere Liste würde sich lesen wie „keine Zuordnung gefunden“ statt „bewusst keiner zugeordnet“. Eine solche Regel kann trotzdem fail ergeben (empty-heading bei einer Überschrift ohne Namen): Ihre Aussage ist dann earl:failed ohne isPartOf, ein Fehlschlag der Regel, nicht eines WCAG-Kriteriums, denn sie beansprucht keins.
  • isPartOf folgt der WCAG-Stufe und -Version des Scans. Ein Kriterium, das der Scan ihretwegen aus rulesResults ausgelassen hat, wird nicht aufgeführt, etwa 1.4.6 (AAA) unter engineOptions.wcagLevel: 'AA' oder 4.1.1, wenn includeRuleIds: ['duplicate-id'] unter WCAG 2.2 lief, da WCAG 2.2 es gestrichen hat; eine Regel, deren sämtliche Kriterien ausgelassen werden, hat kein isPartOf. Die Aussage selbst bleibt, da sie die Regel betrifft. Ein Kriterium, das der Scan meldet, wird aufgeführt, etwa eine per ID genannte Zusammenfassung oberhalb der Stufe oder 4.1.1 in einem 2.2-Scan, der es ausgewählt hat, ebenso jedes Kriterium eines Ergebnisses, das weder engine.wcagLevel noch engine.wcagVersion festhält.
  • assertedBy ist immer vorhanden, außer Sie übergeben assertor: null: Standardmäßig enthält es surea11y und die Engine-Version, die die Ergebnisse tragen. mode erscheint nur, wenn Sie es angeben.

Kriterien-IDs werden aus dem Titel des Kriteriums abgeleitet (Non-text Content → non-text-content). normativeMappings enthält außerdem Verweise auf Understanding-Dokumente, die einen type haben, und Einträge anderer Normen (EN-301-549-Klauseln, die Anforderungen oder Checklistenpunkte eines Packs), die ihren eigenen standard nennen; beide können eine requirement-Nummer mit einem Kriterium teilen. Nur Einträge, deren standard WCAG ist oder fehlt und die keinen type haben, werden als Erfolgskriterien gelesen. Eine Konformitätsstufe ist nicht nötig. isPartOf listet nur WCAG-Kriterien; Einträge anderer Normen werden weggelassen.

Ein kompaktes Ergebnis (engineOptions.output.detail: 'findings', siehe Ausgabeschema) wird als das vollständige Ergebnis gelesen, für das es steht, und daher genauso gemeldet.

Ergebnisse

EngineEARL
passearl:passed
failearl:failed
cantTellearl:cantTell
notApplicableearl:inapplicable

earl:untested hat keine Entsprechung: Eine Regel, die nicht ausgeführt wurde, liefert kein Ergebnis, über das sich etwas aussagen ließe; sie trägt also keine Aussage bei statt einer „untested“-Aussage.

cantTell wird nicht negativ angerechnet. Nach den Konsistenzregeln von ACT darf eine automatische Implementierung bei manchen Beispielen, wenn auch nicht bei allen, „cannot tell“ melden und trotzdem als konsistent gelten. Was eine teilweise konsistente Implementierung nicht darf, ist ein falsch positives Ergebnis erzeugen: ein Beispiel scheitern lassen, das laut Regel bestehen soll oder nicht anwendbar ist. Das ist die Schwelle, auf die es ankommt, und sie ist eine Eigenschaft der Regeln, nicht dieses Berichts. Beim letzten vollständigen Lauf erzeugte die Engine null falsch positive Ergebnisse über die 957 Beispiele hinweg, die das W3C für ihre 60 zugeordneten Regeln veröffentlicht. Siehe ACT_RULE_MAPPING.md, und führen Sie scripts/act-testcase-check.js für die aktuelle Zahl erneut aus, statt sich unbegrenzt auf diese zu verlassen.

Mehrere Ergebnisse, ein Bericht

renderEarlReport nimmt ein Array genauso an wie ein einzelnes Ergebnis, denn ein Bericht über viele Seiten ist der Normalfall:

renderEarlReport([homeResult, checkoutResult, searchResult], { assertor });

Ein Frame-übergreifendes Ergebnis von runa11yCoreAcrossFrames wird so übernommen, wie es ist, allein oder im Array: Jeder Frame, der geantwortet hat, ist ein weiteres Ergebnis und wird zu einem Subjekt unter seiner eigenen URL. Ein Frame unterhalb des obersten, dessen URL ihn nicht unterscheidet, weil sie about:srcdoc oder about:blank ist oder ein anderer Frame des Ergebnisses sie ebenfalls hat, wird nach seiner URL und seinem Pfad benannt, den Selektoren, mit denen SARIF und JUnit ihn benennen, jeweils kodiert und mit / verbunden: about:srcdoc#frame=%23pay, about:srcdoc#frame=%23widget/%23inner. Wo selbst der Pfad geteilt wird, etwa bei den Frames eines Bindings, die keinen Selektor tragen, kommt seine Position unter den Frames des Ergebnisses hinzu, ab 1: about:srcdoc#frame=iframe&n=3. Alles andere, was kein Scan-Ergebnis ist, etwa null oder ein String, wirft einen TypeError.

Ergebnisse mit derselben URL werden zu einem Subjekt zusammengeführt: Wer dieselbe Seite mit unterschiedlichen engineOptions scannt, beschreibt immer noch eine Ressource, und der Kontext kann keine zwei Subjekte mit derselben Quelle ausdrücken. Treffen zwei Ergebnisse eine Aussage zur selben Regel für dieselbe URL, bleibt das schlechtere Ergebnis erhalten: earl:failed, dann earl:cantTell, dann earl:passed, dann earl:inapplicable, sodass keine Reihenfolge der Eingaben einen Fehlschlag verbergen kann. Das gilt auch für dieselbe Seite, gescannt bei zwei Viewport-Breiten: EARL hat keinen Begriff für die Bedingungen, unter denen eine Seite dargestellt wurde, daher behält ein Bericht aus mehreren Breiten für jede Regel einen Fehlschlag bei irgendeiner Breite. Erzeugen Sie einen Bericht pro Breite, wenn die Breiten selbst eine Rolle spielen.

Die Ausgabe ist deterministisch: Subjekte werden nach Quelle sortiert, Aussagen nach Regel-ID, und dieselben Eingaben erzeugen in jeder Reihenfolge byte-identische Ausgabe. Die einzige Ausnahme sind Ergebnisse ohne URL, die nach ihrer Position in der Liste unterschieden werden (about:blank#result-2); geben Sie jedem Ergebnis seine URL, wenn die Reihenfolge keine Rolle spielen soll. Erst dadurch ist ein Diff zwischen zwei Engine-Versionen aussagekräftig.

Optionen

OptionBedeutung
assertor{ name, version }. Der Name ist standardmäßig surea11y, und die Version, angegeben als release des Assertors, standardmäßig die engine.version, die die Ergebnisse tragen, wenn alle dieselbe tragen; Ergebnisse aus verschiedenen Releases erhalten keine, sofern Sie keine angeben. Übergeben Sie null, um assertedBy ganz wegzulassen.
modeEin EARL-Testmodus wie 'earl:automatic'. Entfällt, wenn nicht angegeben.

Siehe auch