Merge pull request #815 from sveltejs/gh-374

a11y checks
pull/828/head
Rich Harris 7 years ago committed by GitHub
commit ad59771bbc

@ -1,7 +1,7 @@
import Block from './Block';
import { trimStart, trimEnd } from '../../utils/trim';
import { assign } from '../../shared/index.js';
import getStaticAttributeValue from '../shared/getStaticAttributeValue';
import getStaticAttributeValue from '../../utils/getStaticAttributeValue';
import { DomGenerator } from './index';
import { Node } from '../../interfaces';
import { State } from './interfaces';

@ -3,7 +3,7 @@ import deindent from '../../../../utils/deindent';
import visitStyleAttribute, { optimizeStyle } from './StyleAttribute';
import { stringify } from '../../../../utils/stringify';
import getExpressionPrecedence from '../../../../utils/getExpressionPrecedence';
import getStaticAttributeValue from '../../../shared/getStaticAttributeValue';
import getStaticAttributeValue from '../../../../utils/getStaticAttributeValue';
import { DomGenerator } from '../../index';
import Block from '../../Block';
import { Node } from '../../../../interfaces';

@ -1,6 +1,6 @@
import deindent from '../../../../utils/deindent';
import flattenReference from '../../../../utils/flattenReference';
import getStaticAttributeValue from '../../../shared/getStaticAttributeValue';
import getStaticAttributeValue from '../../../../utils/getStaticAttributeValue';
import { DomGenerator } from '../../index';
import Block from '../../Block';
import { Node } from '../../../../interfaces';

@ -8,7 +8,7 @@ import visitEventHandler from './EventHandler';
import visitBinding from './Binding';
import visitRef from './Ref';
import * as namespaces from '../../../../utils/namespaces';
import getStaticAttributeValue from '../../../shared/getStaticAttributeValue';
import getStaticAttributeValue from '../../../../utils/getStaticAttributeValue';
import addTransitions from './addTransitions';
import { DomGenerator } from '../../index';
import Block from '../../Block';
@ -102,7 +102,7 @@ export default function visitElement(
if (node._cssRefAttribute) {
block.builders.hydrate.addLine(
`@setAttribute(${name}, "svelte-ref-${node._cssRefAttribute}", ");`
`@setAttribute(${name}, "svelte-ref-${node._cssRefAttribute}", "");`
)
}
}

@ -2,7 +2,7 @@ import attributeLookup from './lookup';
import deindent from '../../../../utils/deindent';
import { stringify } from '../../../../utils/stringify';
import getExpressionPrecedence from '../../../../utils/getExpressionPrecedence';
import getStaticAttributeValue from '../../../shared/getStaticAttributeValue';
import getStaticAttributeValue from '../../../../utils/getStaticAttributeValue';
import { DomGenerator } from '../../index';
import Block from '../../Block';
import { Node } from '../../../../interfaces';

@ -2,7 +2,7 @@ import { DomGenerator } from '../index';
import deindent from '../../../utils/deindent';
import visit from '../visit';
import Block from '../Block';
import getStaticAttributeValue from '../../shared/getStaticAttributeValue';
import getStaticAttributeValue from '../../../utils/getStaticAttributeValue';
import { Node } from '../../../interfaces';
import { State } from '../interfaces';

@ -1,15 +0,0 @@
import { Node } from '../../interfaces';
export default function getStaticAttributeValue(node: Node, name: string) {
const attribute = node.attributes.find(
(attr: Node) => attr.name.toLowerCase() === name
);
if (!attribute) return null;
if (attribute.value.length !== 1 || attribute.value[0].type !== 'Text') {
// TODO catch this in validation phase, give a more useful error (with location etc)
throw new Error(`'${name}' must be a static attribute`);
}
return attribute.value[0].data;
}

@ -54,7 +54,7 @@ export interface CompileOptions {
cascade?: boolean;
hydratable?: boolean;
legacy?: boolean;
customElement: CustomElementOptions | true;
customElement?: CustomElementOptions | true;
onerror?: (error: Error) => void;
onwarn?: (warning: Warning) => void;

@ -0,0 +1,17 @@
import { Node } from '../interfaces';
export default function getStaticAttributeValue(node: Node, name: string) {
const attribute = node.attributes.find(
(attr: Node) => attr.name.toLowerCase() === name
);
if (!attribute) return null;
if (attribute.value.length === 0) return '';
if (attribute.value.length === 1 && attribute.value[0].type === 'Text') {
return attribute.value[0].data;
}
return null;
}

@ -0,0 +1,171 @@
import * as namespaces from '../../utils/namespaces';
import getStaticAttributeValue from '../../utils/getStaticAttributeValue';
import fuzzymatch from '../utils/fuzzymatch';
import validateEventHandler from './validateEventHandler';
import { Validator } from '../index';
import { Node } from '../../interfaces';
const ariaAttributes = 'activedescendant atomic autocomplete busy checked controls describedby disabled dropeffect expanded flowto grabbed haspopup hidden invalid label labelledby level live multiline multiselectable orientation owns posinset pressed readonly relevant required selected setsize sort valuemax valuemin valuenow valuetext'.split(' ');
const ariaAttributeSet = new Set(ariaAttributes);
const ariaRoles = 'alert alertdialog application article banner button checkbox columnheader combobox command complementary composite contentinfo definition dialog directory document form grid gridcell group heading img input landmark link list listbox listitem log main marquee math menu menubar menuitem menuitemcheckbox menuitemradio navigation note option presentation progressbar radio radiogroup range region roletype row rowgroup rowheader scrollbar search section sectionhead select separator slider spinbutton status structure tab tablist tabpanel textbox timer toolbar tooltip tree treegrid treeitem widget window'.split(' ');
const ariaRoleSet = new Set(ariaRoles);
const invisibleElements = new Set(['meta', 'html', 'script', 'style']);
export default function a11y(
validator: Validator,
node: Node,
elementStack: Node[]
) {
if (node.type === 'Text') {
// accessible-emoji
return;
}
if (node.type !== 'Element') return;
const attributeMap = new Map();
node.attributes.forEach((attribute: Node) => {
const name = attribute.name.toLowerCase();
// aria-props
if (name.startsWith('aria-')) {
if (invisibleElements.has(node.name)) {
// aria-unsupported-elements
validator.warn(`A11y: <${node.name}> should not have aria-* attributes`, attribute.start);
}
const type = name.slice(5);
if (!ariaAttributeSet.has(type)) {
const match = fuzzymatch(type, ariaAttributes);
let message = `A11y: Unknown aria attribute 'aria-${type}'`;
if (match) message += ` (did you mean '${match}'?)`;
validator.warn(message, attribute.start);
}
}
// aria-role
if (name === 'role') {
if (invisibleElements.has(node.name)) {
// aria-unsupported-elements
validator.warn(`A11y: <${node.name}> should not have role attribute`, attribute.start);
}
const value = getStaticAttributeValue(node, 'role');
if (value && !ariaRoleSet.has(value)) {
const match = fuzzymatch(value, ariaRoles);
let message = `A11y: Unknown role '${value}'`;
if (match) message += ` (did you mean '${match}'?)`;
validator.warn(message, attribute.start);
}
}
// no-access-key
if (name === 'accesskey') {
validator.warn(`A11y: Avoid using accesskey`, attribute.start);
}
// no-autofocus
if (name === 'autofocus') {
validator.warn(`A11y: Avoid using autofocus`, attribute.start);
}
// scope
if (name === 'scope' && node.name !== 'th') {
validator.warn(`A11y: The scope attribute should only be used with <th> elements`, attribute.start);
}
// tabindex-no-positive
if (name === 'tabindex') {
const value = getStaticAttributeValue(node, 'tabindex');
if (!isNaN(value) && +value > 0) {
validator.warn(`A11y: avoid tabindex values above zero`, attribute.start);
}
}
attributeMap.set(attribute.name, attribute);
});
function shouldHaveAttribute(attributes: string[], name = node.name) {
if (attributes.length === 0 || !attributes.some((name: string) => attributeMap.has(name))) {
const article = /^[aeiou]/.test(attributes[0]) ? 'an' : 'a';
const sequence = attributes.length > 1 ?
attributes.slice(0, -1).join(', ') + ` or ${attributes[attributes.length - 1]}` :
attributes[0];
validator.warn(`A11y: <${name}> element should have ${article} ${sequence} attribute`, node.start);
}
}
function shouldHaveContent() {
if (node.children.length === 0) {
validator.warn(`A11y: <${node.name}> element should have child content`, node.start);
}
}
if (node.name === 'a') {
// anchor-is-valid
const href = attributeMap.get('href');
if (attributeMap.has('href')) {
const value = getStaticAttributeValue(node, 'href');
if (value === '' || value === '#') {
validator.warn(`A11y: '${value}' is not a valid href attribute`, href.start);
}
} else {
validator.warn(`A11y: <a> element should have an href attribute`, node.start);
}
// anchor-has-content
shouldHaveContent();
}
if (node.name === 'img') shouldHaveAttribute(['alt']);
if (node.name === 'area') shouldHaveAttribute(['alt', 'aria-label', 'aria-labelledby']);
if (node.name === 'object') shouldHaveAttribute(['title', 'aria-label', 'aria-labelledby']);
if (node.name === 'input' && getStaticAttributeValue(node, 'type') === 'image') {
shouldHaveAttribute(['alt', 'aria-label', 'aria-labelledby'], 'input type="image"');
}
// heading-has-content
if (/^h[1-6]$/.test(node.name)) {
shouldHaveContent();
if (attributeMap.has('aria-hidden')) {
validator.warn(`A11y: <${node.name}> element should not be hidden`, attributeMap.get('aria-hidden').start);
}
}
// iframe-has-title
if (node.name === 'iframe') {
shouldHaveAttribute(['title']);
}
// no-distracting-elements
if (node.name === 'marquee' || node.name === 'blink') {
validator.warn(`A11y: Avoid <${node.name}> elements`, node.start);
}
if (node.name === 'figcaption') {
const parent = elementStack[elementStack.length - 1];
if (parent) {
if (parent.name !== 'figure') {
validator.warn(`A11y: <figcaption> must be an immediate child of <figure>`, node.start);
} else {
const index = parent.children.indexOf(node);
if (index !== 0 && index !== parent.children.length - 1) {
validator.warn(`A11y: <figcaption> must be first or last child of <figure>`, node.start);
}
}
}
}
}
function getValue(attribute: Node) {
if (attribute.value.length === 0) return '';
if (attribute.value.length === 1 && attribute.value[0].type === 'Text') return attribute.value[0].data;
return null;
}

@ -1,41 +1,27 @@
import * as namespaces from '../../utils/namespaces';
import validateElement from './validateElement';
import validateWindow from './validateWindow';
import a11y from './a11y';
import fuzzymatch from '../utils/fuzzymatch'
import flattenReference from '../../utils/flattenReference';
import { Validator } from '../index';
import { Node } from '../../interfaces';
const svg = /^(?:altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|animateMotion|animateTransform|circle|clipPath|color-profile|cursor|defs|desc|discard|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|foreignObject|g|glyph|glyphRef|hatch|hatchpath|hkern|image|line|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|metadata|missing-glyph|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|solidcolor|stop|switch|symbol|text|textPath|title|tref|tspan|unknown|use|view|vkern)$/;
const meta = new Map([[':Window', validateWindow]]);
export default function validateHtml(validator: Validator, html: Node) {
let elementDepth = 0;
const refs = new Map();
const refCallees: Node[] = [];
const elementStack: Node[] = [];
function visit(node: Node) {
if (node.type === 'Element') {
if (
elementDepth === 0 &&
validator.namespace !== namespaces.svg &&
svg.test(node.name)
) {
validator.warn(
`<${node.name}> is an SVG element did you forget to add { namespace: 'svg' } ?`,
node.start
);
}
a11y(validator, node, elementStack);
if (node.type === 'Element') {
if (meta.has(node.name)) {
return meta.get(node.name)(validator, node, refs, refCallees);
}
elementDepth += 1;
validateElement(validator, node, refs, refCallees);
validateElement(validator, node, refs, refCallees, elementStack);
} else if (node.type === 'EachBlock') {
if (validator.helpers.has(node.context)) {
let c = node.expression.end;
@ -53,16 +39,14 @@ export default function validateHtml(validator: Validator, html: Node) {
}
if (node.children) {
if (node.type === 'Element') elementStack.push(node);
node.children.forEach(visit);
if (node.type === 'Element') elementStack.pop();
}
if (node.else) {
visit(node.else);
}
if (node.type === 'Element') {
elementDepth -= 1;
}
}
html.children.forEach(visit);

@ -1,8 +1,17 @@
import * as namespaces from '../../utils/namespaces';
import validateEventHandler from './validateEventHandler';
import { Validator } from '../index';
import { Node } from '../../interfaces';
export default function validateElement(validator: Validator, node: Node, refs: Map<string, Node[]>, refCallees: Node[]) {
const svg = /^(?:altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|animateMotion|animateTransform|circle|clipPath|color-profile|cursor|defs|desc|discard|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|foreignObject|g|glyph|glyphRef|hatch|hatchpath|hkern|image|line|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|metadata|missing-glyph|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|solidcolor|stop|switch|symbol|text|textPath|title|tref|tspan|unknown|use|view|vkern)$/;
export default function validateElement(
validator: Validator,
node: Node,
refs: Map<string, Node[]>,
refCallees: Node[],
elementStack: Node[]
) {
const isComponent =
node.name === ':Self' || validator.components.has(node.name);
@ -11,6 +20,13 @@ export default function validateElement(validator: Validator, node: Node, refs:
validator.warn(`${node.name} component is not defined`, node.start);
}
if (elementStack.length === 0 && validator.namespace !== namespaces.svg && svg.test(node.name)) {
validator.warn(
`<${node.name}> is an SVG element did you forget to add { namespace: 'svg' } ?`,
node.start
);
}
if (node.name === 'slot') {
const nameAttribute = node.attributes.find((attribute: Node) => attribute.name === 'name');
if (nameAttribute) {

@ -57,14 +57,12 @@ export default function validateJs(validator: Validator, js: Node) {
const match = fuzzymatch(prop.key.name, validPropList);
if (match) {
validator.error(
`Unexpected property '${prop.key
.name}' (did you mean '${match}'?)`,
`Unexpected property '${prop.key.name}' (did you mean '${match}'?)`,
prop.start
);
} else if (/FunctionExpression/.test(prop.value.type)) {
validator.error(
`Unexpected property '${prop.key
.name}' (did you mean to include it in 'methods'?)`,
`Unexpected property '${prop.key.name}' (did you mean to include it in 'methods'?)`,
prop.start
);
} else {

@ -1,6 +1,7 @@
import assert from 'assert';
import * as fs from 'fs';
import { env, normalizeHtml, svelte } from '../helpers.js';
import { parse } from 'acorn';
import { addLineNumbers, env, normalizeHtml, svelte } from '../helpers.js';
function tryRequire(file) {
try {
@ -22,6 +23,15 @@ function normalizeWarning(warning) {
return warning;
}
function checkCodeIsValid(code) {
try {
parse(code);
} catch (err) {
console.error(addLineNumbers(code));
throw new Error(err.message);
}
}
describe('css', () => {
fs.readdirSync('test/css/samples').forEach(dir => {
if (dir[0] === '.') return;
@ -67,6 +77,10 @@ describe('css', () => {
})
);
// check the code is valid
checkCodeIsValid(dom.code);
checkCodeIsValid(ssr.code);
assert.equal(dom.css, ssr.css);
assert.deepEqual(

@ -199,7 +199,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText(state.foo);
this.hydrate();
},

@ -24,7 +24,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText(state.foo);
this.hydrate();
},

@ -187,7 +187,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
div = createElement("div");
this.hydrate();
},

@ -16,7 +16,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
div = createElement("div");
this.hydrate();
},

@ -176,7 +176,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
h1 = createElement( 'h1' );
h1 = createElement("h1");
text = createText("Hello ");
text_1 = createText(state.name);
text_2 = createText("!");

@ -5,7 +5,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
h1 = createElement( 'h1' );
h1 = createElement("h1");
text = createText("Hello ");
text_1 = createText(state.name);
text_2 = createText("!");

@ -180,13 +180,13 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
slot = createElement( 'slot' );
p = createElement( 'p' );
div = createElement("div");
slot = createElement("slot");
p = createElement("p");
text = createText("default fallback content");
text_2 = createText("\n\n\t");
slot_1 = createElement( 'slot' );
p_1 = createElement( 'p' );
slot_1 = createElement("slot");
p_1 = createElement("p");
text_3 = createText("foo fallback content");
this.hydrate();
},

@ -5,13 +5,13 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
slot = createElement( 'slot' );
p = createElement( 'p' );
div = createElement("div");
slot = createElement("slot");
p = createElement("p");
text = createText("default fallback content");
text_2 = createText("\n\n\t");
slot_1 = createElement( 'slot' );
p_1 = createElement( 'p' );
slot_1 = createElement("slot");
p_1 = createElement("p");
text_3 = createText("foo fallback content");
this.hydrate();
},

@ -176,7 +176,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
h1 = createElement( 'h1' );
h1 = createElement("h1");
text = createText("Hello ");
text_1 = createText(state.name);
text_2 = createText("!");

@ -5,7 +5,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
h1 = createElement( 'h1' );
h1 = createElement("h1");
text = createText("Hello ");
text_1 = createText(state.name);
text_2 = createText("!");

@ -202,7 +202,7 @@ function create_main_fragment(state, component) {
}
text = createText("\n\n");
p = createElement( 'p' );
p = createElement("p");
text_1 = createText(state.foo);
},
@ -262,11 +262,11 @@ function create_each_block(state, each_block_value, comment, i, component) {
return {
create: function() {
div = createElement( 'div' );
strong = createElement( 'strong' );
div = createElement("div");
strong = createElement("strong");
text = createText(i);
text_1 = createText("\n\n\t\t");
span = createElement( 'span' );
span = createElement("span");
text_2 = createText(text_2_value);
text_3 = createText(" wrote ");
text_4 = createText(text_4_value);

@ -18,7 +18,7 @@ function create_main_fragment(state, component) {
}
text = createText("\n\n");
p = createElement( 'p' );
p = createElement("p");
text_1 = createText(state.foo);
},
@ -78,11 +78,11 @@ function create_each_block(state, each_block_value, comment, i, component) {
return {
create: function() {
div = createElement( 'div' );
strong = createElement( 'strong' );
div = createElement("div");
strong = createElement("strong");
text = createText(i);
text_1 = createText("\n\n\t\t");
span = createElement( 'span' );
span = createElement("span");
text_2 = createText(text_2_value);
text_3 = createText(" wrote ");
text_4 = createText(text_4_value);

@ -191,7 +191,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
button = createElement( 'button' );
button = createElement("button");
text = createText("foo");
this.hydrate();
},

@ -20,7 +20,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
button = createElement( 'button' );
button = createElement("button");
text = createText("foo");
this.hydrate();
},

@ -218,7 +218,7 @@ function create_if_block(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("foo!");
},
@ -240,7 +240,7 @@ function create_if_block_1(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("not foo!");
},

@ -43,7 +43,7 @@ function create_if_block(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("foo!");
},
@ -65,7 +65,7 @@ function create_if_block_1(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("not foo!");
},

@ -221,7 +221,7 @@ function create_if_block(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("foo!");
},

@ -46,7 +46,7 @@ function create_if_block(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("foo!");
},

@ -172,7 +172,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
div = createElement("div");
this.hydrate();
},

@ -5,7 +5,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
div = createElement("div");
this.hydrate();
},

@ -172,7 +172,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
div = createElement("div");
this.hydrate();
},

@ -5,7 +5,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
div = createElement("div");
this.hydrate();
},

@ -172,7 +172,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
div = createElement("div");
this.hydrate();
},

@ -5,7 +5,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
div = createElement("div");
this.hydrate();
},

@ -172,9 +172,9 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
div = createElement("div");
text = createText("\n");
div_1 = createElement( 'div' );
div_1 = createElement("div");
this.hydrate();
},

@ -5,9 +5,9 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
div = createElement("div");
text = createText("\n");
div_1 = createElement( 'div' );
div_1 = createElement("div");
this.hydrate();
},

@ -180,7 +180,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
input = createElement( 'input' );
input = createElement("input");
this.hydrate();
},

@ -9,7 +9,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
input = createElement( 'input' );
input = createElement("input");
this.hydrate();
},

@ -174,7 +174,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
input = createElement( 'input' );
input = createElement("input");
this.hydrate();
},

@ -5,7 +5,7 @@ function create_main_fragment(state, component) {
return {
create: function() {
input = createElement( 'input' );
input = createElement("input");
this.hydrate();
},

@ -190,17 +190,17 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
div = createElement("div");
if (if_block) if_block.create();
text = createText("\n\n\t");
p = createElement( 'p' );
p = createElement("p");
text_1 = createText("this can be used as an anchor");
text_2 = createText("\n\n\t");
if (if_block_1) if_block_1.create();
text_3 = createText("\n\n\t");
if (if_block_2) if_block_2.create();
text_4 = createText("\n\n\t");
p_1 = createElement( 'p' );
p_1 = createElement("p");
text_5 = createText("so can this");
text_6 = createText("\n\n\t");
if (if_block_3) if_block_3.create();
@ -317,7 +317,7 @@ function create_if_block(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("a");
},
@ -339,7 +339,7 @@ function create_if_block_1(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("b");
},
@ -361,7 +361,7 @@ function create_if_block_2(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("c");
},
@ -383,7 +383,7 @@ function create_if_block_3(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("d");
},
@ -405,7 +405,7 @@ function create_if_block_4(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("e");
},

@ -15,17 +15,17 @@ function create_main_fragment(state, component) {
return {
create: function() {
div = createElement( 'div' );
div = createElement("div");
if (if_block) if_block.create();
text = createText("\n\n\t");
p = createElement( 'p' );
p = createElement("p");
text_1 = createText("this can be used as an anchor");
text_2 = createText("\n\n\t");
if (if_block_1) if_block_1.create();
text_3 = createText("\n\n\t");
if (if_block_2) if_block_2.create();
text_4 = createText("\n\n\t");
p_1 = createElement( 'p' );
p_1 = createElement("p");
text_5 = createText("so can this");
text_6 = createText("\n\n\t");
if (if_block_3) if_block_3.create();
@ -142,7 +142,7 @@ function create_if_block(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("a");
},
@ -164,7 +164,7 @@ function create_if_block_1(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("b");
},
@ -186,7 +186,7 @@ function create_if_block_2(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("c");
},
@ -208,7 +208,7 @@ function create_if_block_3(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("d");
},
@ -230,7 +230,7 @@ function create_if_block_4(state, component) {
return {
create: function() {
p = createElement( 'p' );
p = createElement("p");
text = createText("e");
},

@ -0,0 +1,9 @@
<img src='foo.jpg'>
<map>
<area>
</map>
<object></object>
<input type='image'>

@ -0,0 +1,37 @@
[
{
"message": "A11y: <img> element should have an alt attribute",
"loc": {
"line": 1,
"column": 0
},
"pos": 0
},
{
"message": "A11y: <area> element should have an alt, aria-label or aria-labelledby attribute",
"loc": {
"line": 4,
"column": 1
},
"pos": 28
},
{
"message": "A11y: <object> element should have a title, aria-label or aria-labelledby attribute",
"loc": {
"line": 7,
"column": 0
},
"pos": 43
},
{
"message": "A11y: <input type=\"image\"> element should have an alt, aria-label or aria-labelledby attribute",
"loc": {
"line": 9,
"column": 0
},
"pos": 62
}
]

@ -0,0 +1,8 @@
[{
"message": "A11y: <a> element should have child content",
"loc": {
"line": 1,
"column": 0
},
"pos": 0
}]

@ -0,0 +1,3 @@
<a>not actually a link</a>
<a href=''>invalid</a>
<a href='#'>invalid</a>

@ -0,0 +1,26 @@
[
{
"message": "A11y: <a> element should have an href attribute",
"loc": {
"line": 1,
"column": 0
},
"pos": 0
},
{
"message": "A11y: '' is not a valid href attribute",
"loc": {
"line": 2,
"column": 3
},
"pos": 30
},
{
"message": "A11y: '#' is not a valid href attribute",
"loc": {
"line": 3,
"column": 3
},
"pos": 53
}
]

@ -0,0 +1 @@
<input type="image" aria-labeledby="foo">

@ -0,0 +1,19 @@
[
{
"message": "A11y: Unknown aria attribute 'aria-labeledby' (did you mean 'labelledby'?)",
"loc": {
"line": 1,
"column": 20
},
"pos": 20
},
{
"message": "A11y: <input type=\"image\"> element should have an alt, aria-label or aria-labelledby attribute",
"loc": {
"column": 0,
"line": 1
},
"pos": 0
}
]

@ -0,0 +1,10 @@
[
{
"message": "A11y: Unknown role 'toooltip' (did you mean 'tooltip'?)",
"loc": {
"line": 1,
"column": 5
},
"pos": 5
}
]

@ -0,0 +1,2 @@
<meta aria-hidden="false">
<meta role="tooltip">

@ -0,0 +1,19 @@
[
{
"message": "A11y: <meta> should not have aria-* attributes",
"loc": {
"line": 1,
"column": 6
},
"pos": 6
},
{
"message": "A11y: <meta> should not have role attribute",
"loc": {
"line": 2,
"column": 6
},
"pos": 33
}
]

@ -0,0 +1,19 @@
<figure>
<img src='foo.jpg' alt='a picture of a foo'>
<figcaption>
a foo in its natural habitat
</figcaption>
<p>this should not be here</p>
</figure>
<figure>
<img src='foo.jpg' alt='a picture of a foo'>
<div class='markup-for-styling'>
<figcaption>
this element should be a child of the figure
</figcaption>
</div>
</figure>

@ -0,0 +1,18 @@
[
{
"message": "A11y: <figcaption> must be first or last child of <figure>",
"loc": {
"line": 4,
"column": 1
},
"pos": 57
},
{
"message": "A11y: <figcaption> must be an immediate child of <figure>",
"loc": {
"line": 15,
"column": 2
},
"pos": 252
}
]

@ -0,0 +1,2 @@
<h1></h1>
<h2 aria-hidden>invisible header</h2>

@ -0,0 +1,19 @@
[
{
"message": "A11y: <h1> element should have child content",
"loc": {
"line": 1,
"column": 0
},
"pos": 0
},
{
"message": "A11y: <h2> element should not be hidden",
"loc": {
"line": 2,
"column": 4
},
"pos": 14
}
]

@ -0,0 +1,10 @@
[
{
"message": "A11y: <iframe> element should have a title attribute",
"loc": {
"line": 1,
"column": 0
},
"pos": 0
}
]

@ -0,0 +1,8 @@
[{
"message": "A11y: Avoid using accesskey",
"loc": {
"line": 1,
"column": 5
},
"pos": 5
}]

@ -0,0 +1,8 @@
[{
"message": "A11y: Avoid using autofocus",
"loc": {
"line": 1,
"column": 5
},
"pos": 5
}]

@ -0,0 +1,19 @@
[
{
"message": "A11y: Avoid <marquee> elements",
"loc": {
"line": 1,
"column": 0
},
"pos": 0
},
{
"message": "A11y: Avoid <blink> elements",
"loc": {
"line": 2,
"column": 0
},
"pos": 11
}
]

@ -0,0 +1,10 @@
[
{
"message": "A11y: The scope attribute should only be used with <th> elements",
"loc": {
"line": 1,
"column": 5
},
"pos": 5
}
]

@ -0,0 +1,4 @@
<div tabindex='-1'/>
<div tabindex='0'/>
<div tabindex='1'/>
<div tabindex='{{foo}}'/>

@ -0,0 +1,10 @@
[
{
"message": "A11y: avoid tabindex values above zero",
"loc": {
"line": 3,
"column": 5
},
"pos": 46
}
]
Loading…
Cancel
Save