Commits (19)
......@@ -120,8 +120,8 @@ Node 16.x (current):
Jest compat:
stage: compatibility
dependencies: []
needs: []
dependencies: ["Build"]
needs: ["Build"]
parallel:
matrix:
- VERSION: [24, 25, 26, 27]
......@@ -133,7 +133,8 @@ Jest compat:
- npm install --force jest@${VERSION}.0.0 ts-jest@${VERSION}.0.0
- npm ls jest ts-jest
script:
- npm exec jest -- --no-coverage --ci matchers tests/jest
- for spec in tests/jest/*.spec.ts; do sed 's#../../src/jest#../../jest#g' -i "${spec}"; done
- npm exec jest -- --no-coverage --ci src/jest tests/jest
Module:
stage: compatibility
......
# html-validate changelog
## [5.5.0](https://gitlab.com/html-validate/html-validate/compare/v5.4.1...v5.5.0) (2021-09-05)
### Features
- **dom:** add iterator to `DOMTokenList` ([7bef736](https://gitlab.com/html-validate/html-validate/commit/7bef736bd9902388299e550618192d8465e5f3cc))
- **rules:** `no-missing-references` handles more aria attributes ([2843680](https://gitlab.com/html-validate/html-validate/commit/2843680da32fbe7c95f91c72d2d7607a381d5992))
- **rules:** add `minInitialRank` option to `heading-level` ([7f58572](https://gitlab.com/html-validate/html-validate/commit/7f585721fcb8e744863584cbe6e21130ade198eb)), closes [#132](https://gitlab.com/html-validate/html-validate/issues/132)
### Bug Fixes
- **jest:** synchronous matchers as long as the passed value is synchronous ([0ede9f7](https://gitlab.com/html-validate/html-validate/commit/0ede9f74f073b3d01fafff455cc3674fa1898b40))
- **rules:** `no-missing-references` handles attributes with reference lists ([2afcd86](https://gitlab.com/html-validate/html-validate/commit/2afcd86c1ead8eec42819310bae9990e45122b0e)), closes [#133](https://gitlab.com/html-validate/html-validate/issues/133)
### [5.4.1](https://gitlab.com/html-validate/html-validate/compare/v5.4.0...v5.4.1) (2021-08-29)
### Bug Fixes
......
......@@ -28,4 +28,6 @@ Array [
]
`;
exports[`docs/rules/heading-level.md inline validation: min-initial-rank 1`] = `Array []`;
exports[`docs/rules/heading-level.md inline validation: sectioning-root 1`] = `Array []`;
......@@ -5,7 +5,7 @@ exports[`docs/rules/no-missing-references.md inline validation: correct 1`] = `A
exports[`docs/rules/no-missing-references.md inline validation: incorrect 1`] = `
Array [
Object {
"errorCount": 3,
"errorCount": 4,
"filePath": "inline",
"messages": Array [
Object {
......@@ -53,10 +53,25 @@ Array [
"severity": 2,
"size": 12,
},
Object {
"column": 37,
"context": Object {
"key": "aria-describedby",
"value": "another-missing",
},
"line": 3,
"message": "Element references missing id \\"another-missing\\"",
"offset": 115,
"ruleId": "no-missing-references",
"ruleUrl": "https://html-validate.org/rules/no-missing-references.html",
"selector": "div:nth-child(3)",
"severity": 2,
"size": 15,
},
],
"source": "<label for=\\"missing-input\\"></label>
<div aria-labelledby=\\"missing-text\\"></div>
<div aria-describedby=\\"missing-text\\"></div>",
<div aria-describedby=\\"missing-text another-missing\\"></div>",
"warningCount": 0,
},
]
......
......@@ -5,6 +5,10 @@ markup["incorrect"] = `<h1>Heading 1</h1>
<h3>Subheading</h3>`;
markup["correct"] = `<h1>Heading 1</h1>
<h2>Subheading</h2>`;
markup["min-initial-rank"] = `<nav>
<h2>Navigation</h2>
</nav>
<h1>Heading 1</h1>`;
markup["sectioning-root"] = `<h1>Heading 1</h1>
<h2>Subheading 2</h2>
<dialog>
......@@ -27,6 +31,12 @@ describe("docs/rules/heading-level.md", () => {
const report = htmlvalidate.validateString(markup["correct"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: min-initial-rank", () => {
expect.assertions(1);
const htmlvalidate = new HtmlValidate({"rules":{"heading-level":["error",{"minInitialRank":"h2"}]}});
const report = htmlvalidate.validateString(markup["min-initial-rank"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: sectioning-root", () => {
expect.assertions(1);
const htmlvalidate = new HtmlValidate({"rules":{"heading-level":"error"}});
......
......@@ -3,11 +3,12 @@ import HtmlValidate from "../../../src/htmlvalidate";
const markup: { [key: string]: string } = {};
markup["incorrect"] = `<label for="missing-input"></label>
<div aria-labelledby="missing-text"></div>
<div aria-describedby="missing-text"></div>`;
<div aria-describedby="missing-text another-missing"></div>`;
markup["correct"] = `<label for="my-input"></label>
<div id="verbose-text"></div>
<div id="another-text"></div>
<div aria-labelledby="verbose-text"></div>
<div aria-describedby="verbose-text"></div>
<div aria-describedby="verbose-text another-text"></div>
<input id="my-input">`;
describe("docs/rules/no-missing-references.md", () => {
......
......@@ -33,6 +33,7 @@ This rule takes an optional object:
```json
{
"allowMultipleH1": false,
"minInitialRank": "h1",
"sectioningRoots": ["dialog", "[role=\"dialog\"]"]
}
```
......@@ -41,6 +42,26 @@ This rule takes an optional object:
Set `allowMultipleH1` to `true` to allow multiple `<h1>` elements in a document.
### `minInitialRank`
- type: `"h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "any" | false`
- default: `"h1"`
Sets the allowed initial heading levels (inclusive).
By setting this to `h2` the document may start at `<h2>` and later being followed by `<h1>`:
<validate name="min-initial-rank" rules="heading-level" heading-level='{"minInitialRank": "h2"}'>
<nav>
<h2>Navigation</h2>
</nav>
<h1>Heading 1</h1>
</validate>
Setting this to `"any"` or `false` is equivalent to `"h6"`, i.e. effectively disabling the check for the initial heading level as all possible levels are now allowed.
Note: this does not affect sectioning roots.
Each sectioning root can either continue of the current level or restart at `<h1>`.
### `sectioningRoots`
List of selectors for elements starting new sectioning roots, that is elements with their own outlines.
......@@ -63,3 +84,7 @@ With this option the following is considered valid:
</validate>
[html5-sectioning-root]: https://html.spec.whatwg.org/multipage/sections.html#sectioning-root
## Version history
- 5.5.0 - `minInitialRank` option added.
......@@ -14,9 +14,14 @@ Checked attributes:
- `label[for]`
- `input[list]`
- `*[aria-activedescendant]`
- `*[aria-controls]`
- `*[aria-describedby]`
- `*[aria-details]`
- `*[aria-errormessage]`
- `*[aria-flowto]`
- `*[aria-labelledby]`
- `*[aria-owns]`
## Rule details
......@@ -25,7 +30,7 @@ Examples of **incorrect** code for this rule:
<validate name="incorrect" rules="no-missing-references">
<label for="missing-input"></label>
<div aria-labelledby="missing-text"></div>
<div aria-describedby="missing-text"></div>
<div aria-describedby="missing-text another-missing"></div>
</validate>
Examples of **correct** code for this rule:
......@@ -33,7 +38,12 @@ Examples of **correct** code for this rule:
<validate name="correct" rules="no-missing-references">
<label for="my-input"></label>
<div id="verbose-text"></div>
<div id="another-text"></div>
<div aria-labelledby="verbose-text"></div>
<div aria-describedby="verbose-text"></div>
<div aria-describedby="verbose-text another-text"></div>
<input id="my-input">
</validate>
## Version history
- %version - Extended list of checked aria attributes.
import { Source } from "../src/context";
import { HtmlElement } from "../src/dom";
import HtmlValidate from "../src/htmlvalidate";
import "../src/matchers";
import "../src/jest";
const fileDirectory = "test-files/elements";
const tagNames = [
......
/* eslint-disable-next-line import/export, import/no-unresolved */
export * from "./dist/cjs/matchers";
export * from "./dist/cjs/jest";
module.exports = require("./dist/cjs/matchers");
module.exports = require("./dist/cjs/jest");
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "html-validate",
"version": "5.4.1",
"version": "5.5.0",
"description": "Offline html5 validator",
"keywords": [
"html",
......@@ -18,7 +18,9 @@
},
"license": "MIT",
"author": "David Sveningsson <ext@sidvind.com>",
"sideEffects": false,
"sideEffects": [
"./dist/*/jest.js"
],
"type": "commonjs",
"exports": {
".": {
......@@ -120,7 +122,8 @@
"!src/**/index.ts",
"!src/index.ts",
"!src/browser.ts",
"!src/cli/html-validate.ts"
"!src/cli/html-validate.ts",
"!src/jest/jest.ts"
],
"preset": "@html-validate/jest-config",
"roots": [
......@@ -147,11 +150,11 @@
"semver": "^7.0.0"
},
"devDependencies": {
"@babel/core": "7.15.0",
"@babel/preset-env": "7.15.0",
"@babel/core": "7.15.5",
"@babel/preset-env": "7.15.4",
"@commitlint/cli": "13.1.0",
"@html-validate/commitlint-config": "2.0.0",
"@html-validate/eslint-config": "4.4.5",
"@html-validate/eslint-config": "4.4.6",
"@html-validate/eslint-config-jest": "4.4.2",
"@html-validate/eslint-config-typescript": "4.4.0",
"@html-validate/jest-config": "2.2.0",
......@@ -173,7 +176,7 @@
"@types/node": "11.15.54",
"@types/prompts": "2.0.14",
"@types/semver": "7.3.8",
"autoprefixer": "10.3.3",
"autoprefixer": "10.3.4",
"babar": "0.2.0",
"babelify": "10.0.0",
"bootstrap-sass": "3.4.1",
......@@ -181,7 +184,7 @@
"cssnano": "5.0.8",
"dgeni": "0.4.14",
"dgeni-front-matter": "3.0.0",
"dgeni-packages": "0.29.1",
"dgeni-packages": "0.29.2",
"eslint": "7.32.0",
"font-awesome": "4.7.0",
"front-matter": "4.0.2",
......@@ -206,7 +209,7 @@
"rollup": "2.56.3",
"rollup-plugin-copy": "3.4.0",
"rollup-plugin-dts": "3.0.2",
"sass": "1.38.2",
"sass": "1.39.0",
"semantic-release": "17.4.7",
"serve-static": "1.14.1",
"stringmap": "0.2.2",
......
......@@ -19,7 +19,7 @@ const entrypoints = [
"src/index.ts",
"src/browser.ts",
"src/cli/html-validate.ts",
"src/matchers.ts",
"src/jest/jest.ts",
"src/transform/test-utils.ts",
];
......@@ -27,7 +27,7 @@ const entrypoints = [
const types = [
"dist/types/index.d.ts",
"dist/types/browser.d.ts",
"dist/types/matchers.d.ts",
"dist/types/jest/jest.d.ts",
"dist/types/transform/test-utils.d.ts",
];
......@@ -69,6 +69,10 @@ function manualChunks(id) {
return "cli";
}
if (rel.startsWith("jest/")) {
return "jest-lib";
}
return "core";
}
......
......@@ -2,7 +2,7 @@ import fs from "fs";
import path from "path";
import { CLI } from "./cli";
import { IsIgnored } from "./is-ignored";
import "../matchers";
import "../jest";
describe("integration tests", () => {
let ignored: IsIgnored;
......
......@@ -142,4 +142,29 @@ describe("DOMTokenList", () => {
expect(list.contains("spam")).toBeFalsy();
});
});
describe("iterator()", () => {
it("should loop over all items and locations", () => {
expect.assertions(1);
const list = new DOMTokenList("foo bar baz", location);
const result = Array.from(list.iterator());
expect(result).toEqual([
{
index: 0,
item: "foo",
location: expect.objectContaining({ line: 1, column: 1, size: 3 }),
},
{
index: 1,
item: "bar",
location: expect.objectContaining({ line: 1, column: 5, size: 3 }),
},
{
index: 2,
item: "baz",
location: expect.objectContaining({ line: 1, column: 9, size: 3 }),
},
]);
});
});
});
......@@ -79,4 +79,14 @@ export class DOMTokenList extends Array<string> {
public contains(token: string): boolean {
return this.includes(token);
}
public *iterator(): Generator<{ index: number; item: string; location: Location }> {
for (let index = 0; index < this.length; index++) {
/* eslint-disable @typescript-eslint/no-non-null-assertion */
const item = this.item(index)!;
const location = this.location(index)!;
/* eslint-enable @typescript-eslint/no-non-null-assertion */
yield { index, item, location };
}
}
}
......@@ -2,7 +2,7 @@ import { Config, ResolvedConfig, RuleOptions, Severity } from "../config";
import { Source } from "../context";
import { HtmlElement } from "../dom";
import { InvalidTokenError } from "../lexer";
import "../matchers";
import "../jest";
import { MetaTable } from "../meta";
import { Parser, ParserError } from "../parser";
import { Reporter } from "../reporter";
......
......@@ -2,7 +2,7 @@ import HtmlValidate from "./htmlvalidate";
import { Source } from "./context";
import { TRANSFORMER_API } from "./transform";
import { Plugin } from "./plugin";
import "./matchers";
import "./jest";
import { Rule } from "./rule";
import { DOMReadyEvent } from "./event";
......