Skip to content

Commit

Permalink
feat: check expected values of schema.org a11y metadata
Browse files Browse the repository at this point in the history
Fixes #202
  • Loading branch information
rdeltour committed Jul 4, 2019
1 parent df3c666 commit a5452c3
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 21 deletions.
136 changes: 123 additions & 13 deletions packages/ace-core/src/checker/checker-epub.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,100 @@ const ASSERTED_BY = 'Ace';
const MODE = 'automatic';
const KB_BASE = 'http://kb.daisy.org/publishing/';

const A11Y_META = {
'schema:accessMode': {
required: true,
allowedValues: [
'auditory',
'chartOnVisual',
'chemOnVisual',
'colorDependent',
'diagramOnVisual',
'mathOnVisual',
'musicOnVisual',
'tactile',
'textOnVisual',
'textual',
'visual',
]
},
'schema:accessModeSufficient': {
recommended: true,
allowedValues: [
'auditory',
'tactile',
'textual',
'visual',
]
},
'schema:accessibilityAPI': {
allowedValues: [
'ARIA'
]
},
'schema:accessibilityControl': {
allowedValues: [
'fullKeyboardControl',
'fullMouseControl',
'fullSwitchControl',
'fullTouchControl',
'fullVideoControl',
'fullVoiceControl',
]
},
'schema:accessibilityFeature': {
required: true,
allowedValues: [
'alternativeText',
'annotations',
'audioDescription',
'bookmarks',
'braille',
'captions',
'ChemML',
'describedMath',
'displayTransformability',
'highContrastAudio',
'highContrastDisplay',
'index',
'largePrint',
'latex',
'longDescription',
'MathML',
'none',
'printPageNumbers',
'readingOrder',
'rubyAnnotations',
'signLanguage',
'structuralNavigation',
'synchronizedAudioText',
'tableOfContents',
'taggedPDF',
'tactileGraphic',
'tactileObject',
'timingControl',
'transcript',
'ttsMarkup',
'unlocked ',

This comment has been minimized.

Copy link
@thiagoeec

thiagoeec Jul 5, 2019

Contributor

Must remove the trailing space on 'unlocked '.

This comment has been minimized.

Copy link
@rdeltour

rdeltour Jul 5, 2019

Author Member

damn, good catch!

],
},
'schema:accessibilityHazard': {
allowedValues: [
'flashing',
'noFlashingHazard',
'motionSimulation',
'noMotionSimulationHazard',
'sound',
'noSoundHazard',
'unknown',
'none',
]
},
'schema:accessibilitySummary': {
required: true,
}
};

function asString(arrayOrString) {
if (Array.isArray(arrayOrString) && arrayOrString.length > 0) {
return asString(arrayOrString[0]);
Expand Down Expand Up @@ -43,7 +137,7 @@ function newViolation({ impact = 'serious', title, testDesc, resDesc, kbPath, kb
function newMetadataAssertion(name, impact = 'serious') {
return newViolation({
impact,
title: `metadata-${name.toLowerCase().replace(':', '-')}`,
title: `metadata-${name.toLowerCase().replace('schema:', '')}`,
testDesc: localize("checkepub.metadataviolation.testdesc", { name, interpolation: { escapeValue: false } }),
resDesc: localize("checkepub.metadataviolation.resdesc", { name, interpolation: { escapeValue: false } }),
kbPath: 'docs/metadata/schema-org.html',
Expand All @@ -53,18 +147,34 @@ function newMetadataAssertion(name, impact = 'serious') {
}

function checkMetadata(assertions, epub) {
// Required metadata
[
'schema:accessMode',
'schema:accessibilityFeature',
'schema:accessibilitySummary',
].filter(meta => epub.metadata[meta] === undefined)
.forEach(meta => assertions.withAssertions(newMetadataAssertion(meta)));
// Recommended metadata
[
'schema:accessModeSufficient',
].filter(meta => epub.metadata[meta] === undefined)
.forEach(meta => assertions.withAssertions(newMetadataAssertion(meta, 'moderate')));
// Check metadata values
for (const name in A11Y_META) {
const meta = A11Y_META[name];
var values = epub.metadata[name];
if (values === undefined) {
// Report missing metadata if it is required or recommended
if (meta.required) assertions.withAssertions(newMetadataAssertion(name));
if (meta.recommended) assertions.withAssertions(newMetadataAssertion(name, 'moderate'));
} else if (meta.allowedValues) {
// Check metadata values are allowed
// see https://www.w3.org/wiki/WebSchemas/Accessibility
if (!Array.isArray(values)) {
values = [values]
}
values.filter(value => !meta.allowedValues.includes(value))
.forEach(value => {
assertions.withAssertions(newViolation({
impact: 'moderate',
title: `metadata-${name.toLowerCase().replace('schema:', '')}-invalid`,
testDesc: localize("checkepub.metadatainvalid.testdesc", { value, name, interpolation: { escapeValue: false } }),
resDesc: localize("checkepub.metadatainvalid.resdesc", { name, interpolation: { escapeValue: false } }),
kbPath: 'docs/metadata/schema-org.html',
kbTitle: localize("checkepub.metadatainvalid.kbtitle"),
ruleDesc: localize("checkepub.metadatainvalid.ruledesc", { name, interpolation: { escapeValue: false } })
}))
});
}
}
}

function checkTitle(assertions, epub) {
Expand Down
6 changes: 6 additions & 0 deletions packages/ace-core/src/l10n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
"ruledesc": "Publications must declare the '{{name}}' metadata",
"kbtitle": "Schema.org Accessibility Metadata"
},
"metadatainvalid": {
"testdesc": "Value '{{value}}' is invalid for '{{name}}' metadata",
"resdesc": "Use one of the metadata values defined by schema.org",
"ruledesc": "'{{name}}' metadata must be set to one of the expected values",
"kbtitle": "Schema.org Accessibility Metadata"
},
"titleviolation": {
"testdesc": "Ensures the EPUB has a title",
"resdesc": "Add a 'dc:title' metadata property to the Package Document",
Expand Down
6 changes: 6 additions & 0 deletions packages/ace-core/src/l10n/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
"ruledesc": "Toute publication doit déclarer la métadonnée '{{name}}'",
"kbtitle": "Métadonnées d'accessibilité Schema.org"
},
"metadatainvalid": {
"testdesc": "La valeur '{{value}}' est incorrecte pour la métadonnée '{{name}}'",
"resdesc": "Utiliser l’une des valeurs définie par schema.org",
"ruledesc": "La métadonnée '{{name}}' doit avoir une valeur parmi les valeurs prédéfinies",
"kbtitle": "Métadonnées d'accessibilité Schema.org"
},
"titleviolation": {
"testdesc": "Vérifie que le EPUB a un titre",
"resdesc": "Ajouter la métadonnée 'dc:title' au Document de Package",
Expand Down
9 changes: 1 addition & 8 deletions tests/__tests__/axe-rules.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const path = require('path');
const tmp = require('tmp');

const runAce = require('../runAceJS');
import { findAssertionsForDoc } from "../utils";

tmp.setGracefulCleanup();

Expand Down Expand Up @@ -38,14 +39,6 @@ function ace(epub, options = {}) {
.catch(err => console.log(err));
}

function findAssertionsForDoc(report, path) {
for (const assertion of report.assertions) {
if(assertion['earl:testSubject'].url === path) {
return assertion.assertions;
}
}
}

test('`bypass` rule is disabled', async () => {
const report = await ace('../data/axerule-bypass');
expect(report['earl:result']['earl:outcome']).toEqual('pass');
Expand Down
29 changes: 29 additions & 0 deletions tests/__tests__/epub-rules.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const path = require('path');
const tmp = require('tmp');

const runAce = require('../runAceJS');
import { findAssertionsForDoc } from "../utils";

tmp.setGracefulCleanup();

Expand Down Expand Up @@ -50,3 +51,31 @@ describe('page list and breaks', () => {
expect(report['earl:result']['earl:outcome']).toEqual('pass');
});
});

describe('accessibility metadata', () => {

test('missing metadata is reported', async () => {
const report = await ace('../data/epubrules-metadata');
expect(report['earl:result']['earl:outcome']).toEqual('fail');
const assertions = findAssertionsForDoc(report, 'EPUB/package.opf');
expect(assertions).toEqual(expect.arrayContaining([
expect.objectContaining({
'earl:test': expect.objectContaining({
'dct:title': 'metadata-accessibilitysummary',
}),
}),
]));
});
test('incorrect metadata value is reported', async () => {
const report = await ace('../data/epubrules-metadata');
expect(report['earl:result']['earl:outcome']).toEqual('fail');
const assertions = findAssertionsForDoc(report, 'EPUB/package.opf');
expect(assertions).toEqual(expect.arrayContaining([
expect.objectContaining({
'earl:test': expect.objectContaining({
'dct:title': 'metadata-accessmode-invalid',
}),
}),
]));
});
});
9 changes: 9 additions & 0 deletions tests/data/epubrules-metadata/EPUB/content_001.xhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="en">
<head>
<title>Minimal EPUB</title>
</head>
<body>
<h1>Loomings</h1>
<p>Call me Ishmael.</p>
</body>
</html>
12 changes: 12 additions & 0 deletions tests/data/epubrules-metadata/EPUB/nav.xhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="en">
<head>
<title>Minimal Nav</title>
</head>
<body>
<nav epub:type="toc">
<ol>
<li><a href="content_001.xhtml">content 001</a></li>
</ol>
</nav>
</body>
</html>
25 changes: 25 additions & 0 deletions tests/data/epubrules-metadata/EPUB/package.opf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="en" unique-identifier="uid">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title id="title">Minimal EPUB 3.0</dc:title>
<dc:language>en</dc:language>
<dc:identifier id="uid">NOID</dc:identifier>
<meta property="dcterms:modified">2017-01-01T00:00:01Z</meta>
<meta property="schema:accessibilityFeature">structuralNavigation</meta>
<!-- Fail #1: missing accessibility summary -->
<!-- <meta property="schema:accessibilitySummary">everything OK!</meta> -->
<meta property="schema:accessibilityHazard">noFlashingHazard</meta>
<meta property="schema:accessibilityHazard">noSoundHazard</meta>
<meta property="schema:accessibilityHazard">noMotionSimulationHazard</meta>
<!-- Fail #2: bad metadata value -->
<meta property="schema:accessMode">text</meta>
<meta property="schema:accessModeSufficient">textual</meta>
</metadata>
<manifest>
<item id="nav" href="nav.xhtml" media-type="application/xhtml+xml" properties="nav"/>
<item id="content_001" href="content_001.xhtml" media-type="application/xhtml+xml"/>
</manifest>
<spine>
<itemref idref="content_001" />
</spine>
</package>
6 changes: 6 additions & 0 deletions tests/data/epubrules-metadata/META-INF/container.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
<rootfiles>
<rootfile full-path="EPUB/package.opf" media-type="application/oebps-package+xml"/>
</rootfiles>
</container>
1 change: 1 addition & 0 deletions tests/data/epubrules-metadata/mimetype
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
application/epub+zip
9 changes: 9 additions & 0 deletions tests/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

export function findAssertionsForDoc(report, path) {
for (const assertion of report.assertions) {
if (assertion['earl:testSubject'].url === path) {
return assertion.assertions;
}
}
}

0 comments on commit a5452c3

Please sign in to comment.