...
 
Commits (22)
......@@ -89,6 +89,7 @@ Docs:
.compat: &compat
stage: compatibility
dependencies: []
needs: ["Build"]
before_script:
- npm ci
script:
......@@ -112,7 +113,7 @@ Node 12.x (current):
Release:
stage: release
only:
- master
- web
variables:
GIT_AUTHOR_NAME: "David Sveningsson"
GIT_AUTHOR_EMAIL: "ext@sidvind.com"
......@@ -125,34 +126,21 @@ Release:
stage: postrelease
only:
- tags
dependencies: []
before_script:
- sleep 10
variables:
UPSTREAM_VERSION: "${CI_COMMIT_TAG}"
grunt-html-validate:
<<: *downstream
variables:
PROJECT_ID: 10926528
script:
- scripts/trigger-downstream "${PROJECT_ID}" "${DOWNSTREAM_GRUNT_TOKEN}" "master" "${CI_COMMIT_TAG}"
trigger: html-validate/grunt-html-validate
html-validate-angular:
<<: *downstream
variables:
PROJECT_ID: 10598170
script:
- scripts/trigger-downstream "${PROJECT_ID}" "${DOWNSTREAM_ANGULAR_TOKEN}" "master" "${CI_COMMIT_TAG}"
trigger: html-validate/html-validate-angular
html-validate-vue:
<<: *downstream
variables:
PROJECT_ID: 10926311
script:
- scripts/trigger-downstream "${PROJECT_ID}" "${DOWNSTREAM_VUE_TOKEN}" "master" "${CI_COMMIT_TAG}"
trigger: html-validate/html-validate-vue
protractor-html-validate:
<<: *downstream
variables:
PROJECT_ID: 10443680
script:
- scripts/trigger-downstream "${PROJECT_ID}" "${DOWNSTREAM_PROTRACTOR_TOKEN}" "master" "${CI_COMMIT_TAG}"
trigger: html-validate/protractor-html-validate
# html-validate changelog
# [1.6.0](https://gitlab.com/html-validate/html-validate/compare/v1.5.1...v1.6.0) (2019-09-01)
### Bug Fixes
- **matchers:** typo in error message ([daeabba](https://gitlab.com/html-validate/html-validate/commit/daeabba))
### Features
- **matchers:** optionally test context ([44fcf47](https://gitlab.com/html-validate/html-validate/commit/44fcf47))
## [1.5.1](https://gitlab.com/html-validate/html-validate/compare/v1.5.0...v1.5.1) (2019-08-20)
### Bug Fixes
......
This diff is collapsed.
{
"name": "html-validate",
"version": "1.5.1",
"version": "1.6.0",
"description": "html linter",
"keywords": [
"html",
......@@ -111,7 +111,7 @@
"@semantic-release/exec": "3.3.6",
"@semantic-release/git": "7.0.16",
"@semantic-release/gitlab": "3.1.7",
"@semantic-release/npm": "5.1.13",
"@semantic-release/npm": "5.1.15",
"@semantic-release/release-notes-generator": "7.3.0",
"@types/babel__code-frame": "7.0.1",
"@types/estree": "0.0.39",
......@@ -119,7 +119,7 @@
"@types/jest": "24.0.18",
"@types/json-merge-patch": "0.0.4",
"@types/minimist": "1.2.0",
"@types/node": "11.13.19",
"@types/node": "11.13.20",
"@typescript-eslint/eslint-plugin": "2.0.0",
"@typescript-eslint/parser": "2.0.0",
"autoprefixer": "9.6.1",
......@@ -133,8 +133,8 @@
"eslint-config-sidvind": "1.3.2",
"eslint-plugin-array-func": "3.1.3",
"eslint-plugin-import": "2.18.2",
"eslint-plugin-jest": "22.15.1",
"eslint-plugin-node": "9.1.0",
"eslint-plugin-jest": "22.16.0",
"eslint-plugin-node": "9.2.0",
"eslint-plugin-prettier": "3.1.0",
"eslint-plugin-security": "1.4.0",
"eslint-plugin-sonarjs": "0.4.0",
......@@ -146,23 +146,23 @@
"grunt-contrib-copy": "1.0.0",
"grunt-postcss": "0.9.0",
"grunt-sass": "3.1.0",
"highlight.js": "9.15.9",
"highlight.js": "9.15.10",
"husky": "3.0.4",
"jest": "24.9.0",
"jest-diff": "24.9.0",
"jest-junit": "7.0.0",
"jest-junit": "8.0.0",
"jquery": "3.4.1",
"lint-staged": "9.2.3",
"lint-staged": "9.2.5",
"load-grunt-tasks": "5.1.0",
"prettier": "1.18.2",
"sass": "1.22.10",
"semantic-release": "15.13.21",
"semantic-release": "15.13.24",
"serve-static": "1.14.1",
"strip-ansi": "5.2.0",
"ts-jest": "24.0.2",
"tslint": "5.18.0",
"tslint": "5.19.0",
"tslint-config-prettier": "1.18.0",
"typescript": "3.5.3"
"typescript": "3.6.2"
},
"jest": {
"collectCoverage": true,
......@@ -170,7 +170,6 @@
"src/**/*.ts",
"!src/**/*.spec.ts",
"!src/**/index.ts",
"!src/matchers.ts",
"!src/shim.ts",
"!src/cli/html-validate.ts"
],
......
#!/bin/sh
if [ $# -lt 4 ]; then
echo 'usage: trigger-downstream ID TOKEN BRANCH VERSION'
exit 1
fi
id="$1"
token="$2"
branch="$3"
version="$4"
exec curl --silent --show-error --fail \
--stderr /dev/stderr \
--output /dev/null \
-X POST \
-F "token=${token}" \
-F "ref=${branch}" \
-F "variables[UPSTREAM_VERSION]=${version}" \
https://gitlab.com/api/v4/projects/${id}/trigger/pipeline
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`toBeInvalid() should fail if report is valid 1`] = `"Result should be invalid but had no errors"`;
exports[`toBeToken() should fail if token doesn't match 1`] = `
"expect(received).toBeToken(expected)
Expected token to equal:
ObjectContaining {\\"type\\": \\"TAG_CLOSE\\"}
Received:
{\\"data\\": [\\"<foo\\", \\"\\", \\"foo\\"], \\"location\\": {\\"column\\": 2, \\"filename\\": \\"inline\\", \\"line\\": 1, \\"offset\\": 1}, \\"type\\": 6}
Difference:
- Expected
+ Received
- ObjectContaining {
- \\"type\\": \\"TAG_CLOSE\\",
+ Object {
+ \\"data\\": Array [
+ \\"<foo\\",
+ \\"\\",
+ \\"foo\\",
+ ],
+ \\"location\\": Object {
+ \\"column\\": 2,
+ \\"filename\\": \\"inline\\",
+ \\"line\\": 1,
+ \\"offset\\": 1,
+ },
+ \\"type\\": 6,
}"
`;
exports[`toBeValid() should fail if report is invalid 1`] = `"Result should be successful but had error \\"mock message\\""`;
exports[`toHaveError() should fail if error has mismatched context 1`] = `
"expect(received).toHaveError(expected)
Expected token to equal:
[ObjectContaining {\\"context\\": {\\"foo\\": \\"spam\\"}, \\"message\\": \\"mock message\\", \\"ruleId\\": \\"my-rule\\"}]
Received:
[{\\"column\\": 15, \\"context\\": {\\"foo\\": \\"bar\\"}, \\"line\\": 2, \\"message\\": \\"mock message\\", \\"offset\\": 43, \\"ruleId\\": \\"my-rule\\", \\"severity\\": 2, \\"size\\": 12}]
Difference:
- Expected
+ Received
Array [
- ObjectContaining {
+ Object {
+ \\"column\\": 15,
\\"context\\": Object {
- \\"foo\\": \\"spam\\",
+ \\"foo\\": \\"bar\\",
},
+ \\"line\\": 2,
\\"message\\": \\"mock message\\",
+ \\"offset\\": 43,
\\"ruleId\\": \\"my-rule\\",
+ \\"severity\\": 2,
+ \\"size\\": 12,
},
]"
`;
exports[`toHaveError() should fail if error is missing 1`] = `
"expect(received).toHaveError(expected)
Expected token to equal:
[ObjectContaining {\\"message\\": \\"asdf\\", \\"ruleId\\": \\"asdf\\"}]
Received:
[{\\"column\\": 15, \\"context\\": {\\"foo\\": \\"bar\\"}, \\"line\\": 2, \\"message\\": \\"mock message\\", \\"offset\\": 43, \\"ruleId\\": \\"my-rule\\", \\"severity\\": 2, \\"size\\": 12}]
Difference:
- Expected
+ Received
Array [
- ObjectContaining {
- \\"message\\": \\"asdf\\",
- \\"ruleId\\": \\"asdf\\",
+ Object {
+ \\"column\\": 15,
+ \\"context\\": Object {
+ \\"foo\\": \\"bar\\",
+ },
+ \\"line\\": 2,
+ \\"message\\": \\"mock message\\",
+ \\"offset\\": 43,
+ \\"ruleId\\": \\"my-rule\\",
+ \\"severity\\": 2,
+ \\"size\\": 12,
},
]"
`;
exports[`toHaveErrors() should fail if error any missing 1`] = `
"expect(received).toHaveErrors(expected)
Expected token to equal:
[ObjectContaining {\\"message\\": \\"mock message\\", \\"ruleId\\": \\"my-rule\\"}, ObjectContaining {\\"message\\": \\"spam\\", \\"ruleId\\": \\"spam\\"}]
Received:
[{\\"column\\": 15, \\"context\\": {\\"foo\\": \\"bar\\"}, \\"line\\": 2, \\"message\\": \\"mock message\\", \\"offset\\": 43, \\"ruleId\\": \\"my-rule\\", \\"severity\\": 2, \\"size\\": 12}, {\\"column\\": 15, \\"line\\": 2, \\"message\\": \\"another message\\", \\"offset\\": 43, \\"ruleId\\": \\"another-rule\\", \\"severity\\": 2, \\"size\\": 12}]
Difference:
- Expected
+ Received
Array [
- ObjectContaining {
+ Object {
+ \\"column\\": 15,
+ \\"context\\": Object {
+ \\"foo\\": \\"bar\\",
+ },
+ \\"line\\": 2,
\\"message\\": \\"mock message\\",
+ \\"offset\\": 43,
\\"ruleId\\": \\"my-rule\\",
+ \\"severity\\": 2,
+ \\"size\\": 12,
},
- ObjectContaining {
- \\"message\\": \\"spam\\",
- \\"ruleId\\": \\"spam\\",
+ Object {
+ \\"column\\": 15,
+ \\"line\\": 2,
+ \\"message\\": \\"another message\\",
+ \\"offset\\": 43,
+ \\"ruleId\\": \\"another-rule\\",
+ \\"severity\\": 2,
+ \\"size\\": 12,
},
]"
`;
import { Severity } from "./config";
import { Token, TokenType } from "./lexer";
import "./matchers";
import { Report, Reporter } from "./reporter";
import stripAnsi from "strip-ansi";
let reportOk: Report;
let reportError: Report;
let reportMultipleErrors: Report;
beforeEach(() => {
const reporter = new Reporter();
reportOk = reporter.save();
/* generate first report with a single error */
reporter.addManual("inline", {
ruleId: "my-rule",
severity: Severity.ERROR,
message: "mock message",
line: 2,
column: 15,
offset: 43,
size: 12,
context: {
foo: "bar",
},
});
reportError = reporter.save();
/* add another to the report with multiple errors */
reporter.addManual("inline", {
ruleId: "another-rule",
severity: Severity.ERROR,
message: "another message",
line: 2,
column: 15,
offset: 43,
size: 12,
});
reportMultipleErrors = reporter.save();
});
describe("toBeValid()", () => {
it("should pass if report is valid", async () => {
expect.assertions(1);
await expect(reportOk).toBeValid();
});
it("should fail if report is invalid", async () => {
expect.assertions(3);
let error: Error;
try {
await expect(reportError).toBeValid();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toMatchSnapshot();
});
});
describe("toBeInvalid()", () => {
it("should pass if report is invalid", async () => {
expect.assertions(1);
await expect(reportError).toBeInvalid();
});
it("should fail if report is valid", async () => {
expect.assertions(3);
let error: Error;
try {
await expect(reportOk).toBeInvalid();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toMatchSnapshot();
});
});
describe("toHaveError()", () => {
it("should pass if error is preset", async () => {
expect.assertions(1);
await expect(reportError).toHaveError("my-rule", "mock message");
});
it("should pass if error have matching context", async () => {
expect.assertions(1);
await expect(reportError).toHaveError("my-rule", "mock message", {
foo: "bar",
});
});
it("should fail if error is missing", async () => {
expect.assertions(3);
let error: Error;
try {
await expect(reportError).toHaveError("asdf", "asdf");
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(stripAnsi(error.message)).toMatchSnapshot();
});
it("should fail if error has mismatched context", async () => {
expect.assertions(3);
let error: Error;
try {
await expect(reportError).toHaveError("my-rule", "mock message", {
foo: "spam",
});
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(stripAnsi(error.message)).toMatchSnapshot();
});
});
describe("toHaveErrors()", () => {
it("should pass if error is preset", async () => {
expect.assertions(1);
await expect(reportError).toHaveErrors([["my-rule", "mock message"]]);
});
it("should pass if error have matching context", async () => {
expect.assertions(1);
await expect(reportError).toHaveErrors([
{ ruleId: "my-rule", message: "mock message", context: { foo: "bar" } },
]);
});
it("should pass if all errors are preset", async () => {
expect.assertions(1);
await expect(reportMultipleErrors).toHaveErrors([
["my-rule", "mock message"],
["another-rule", "another message"],
]);
});
it("should fail if error any missing", async () => {
expect.assertions(3);
let error: Error;
try {
await expect(reportMultipleErrors).toHaveErrors([
["my-rule", "mock message"],
["spam", "spam"],
]);
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(stripAnsi(error.message)).toMatchSnapshot();
});
});
describe("toBeToken()", () => {
const token: Token = {
type: TokenType.TAG_OPEN,
data: ["<foo", "", "foo"],
location: {
filename: "inline",
line: 1,
column: 2,
offset: 1,
},
};
it("should pass if token matches", async () => {
expect.assertions(1);
await expect({ value: token }).toBeToken({
type: TokenType.TAG_OPEN,
});
});
it("should fail if token doesn't match", async () => {
expect.assertions(3);
let error: Error;
try {
await expect({ value: token }).toBeToken({
type: TokenType.TAG_CLOSE,
});
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(stripAnsi(error.message)).toMatchSnapshot();
});
});
......@@ -16,17 +16,27 @@ declare global {
toBeValid(): R;
toBeInvalid(): R;
toBeToken(expected: TokenMatcher): R;
toHaveError(ruleId: string, message: string): R;
toHaveError(ruleId: string, message: string, context?: any): R;
toHaveErrors(errors: Array<[string, string] | {}>): R;
}
}
}
/**
* Takes all messages from all files and flattens to a single array.
*/
function flattenMessages(report: Report): Message[] {
return report.results.reduce((aggregated: Message[], result: Result) => {
return aggregated.concat(result.messages);
}, []);
}
function toBeValid(report: Report): jest.CustomMatcherResult {
if (report.valid) {
return {
pass: true,
message: () => "Result should not contain error",
message: /* istanbul ignore next */ () =>
"Result should not contain error",
};
} else {
const firstError = report.results[0].messages[0];
......@@ -42,12 +52,13 @@ function toBeInvalid(report: Report): jest.CustomMatcherResult {
if (report.valid) {
return {
pass: false,
message: () => "Result should be successful",
message: () => "Result should be invalid but had no errors",
};
} else {
return {
pass: true,
message: () => "Result should not contain error",
message: /* istanbul ignore next */ () =>
"Result should not contain error",
};
}
}
......@@ -55,15 +66,16 @@ function toBeInvalid(report: Report): jest.CustomMatcherResult {
function toHaveError(
report: Report,
ruleId: any,
message: any
message: any,
context?: any
): jest.CustomMatcherResult {
const actual = report.results.reduce(
(aggregated: Message[], result: Result) => {
return aggregated.concat(result.messages);
},
[]
);
const matcher = [expect.objectContaining({ ruleId, message })];
const actual = flattenMessages(report);
const expected: any = { ruleId, message };
if (context) {
expected.context = context;
}
const matcher = [expect.objectContaining(expected)];
const pass = this.equals(actual, matcher);
const diffString = diff(matcher, actual, { expand: this.expand });
const resultMessage = (): string =>
......@@ -73,7 +85,9 @@ function toHaveError(
` ${this.utils.printExpected(matcher)}\n` +
"Received:\n" +
` ${this.utils.printReceived(actual)}` +
(diffString ? `\n\nDifference:\n\n${diffString}` : "");
/* istanbul ignore next */ (diffString
? `\n\nDifference:\n\n${diffString}`
: "");
return { pass, message: resultMessage };
}
......@@ -82,12 +96,7 @@ function toHaveErrors(
report: Report,
errors: Array<[string, string] | {}>
): jest.CustomMatcherResult {
const actual = report.results.reduce(
(aggregated: Message[], result: Result) => {
return aggregated.concat(result.messages);
},
[]
);
const actual = flattenMessages(report);
const matcher = errors.map(entry => {
if (Array.isArray(entry)) {
const [ruleId, message] = entry;
......@@ -105,7 +114,9 @@ function toHaveErrors(
` ${this.utils.printExpected(matcher)}\n` +
"Received:\n" +
` ${this.utils.printReceived(actual)}` +
(diffString ? `\n\nDifference:\n\n${diffString}` : "");
/* istanbul ignore next */ (diffString
? `\n\nDifference:\n\n${diffString}`
: "");
return { pass, message: resultMessage };
}
......@@ -113,10 +124,12 @@ function toHaveErrors(
function toBeToken(actual: any, expected: any): jest.CustomMatcherResult {
const token = actual.value;
// istanbul ignore next: TokenMatcher requires "type" property to be set, this is just a failsafe
if (token.type) {
token.type = TokenType[token.type];
}
// istanbul ignore next: TokenMatcher requires "type" property to be set, this is just a failsafe
if (expected.type) {
expected.type = TokenType[expected.type];
}
......@@ -131,7 +144,9 @@ function toBeToken(actual: any, expected: any): jest.CustomMatcherResult {
` ${this.utils.printExpected(matcher)}\n` +
"Received:\n" +
` ${this.utils.printReceived(token)}` +
(diffString ? `\n\nDifference:\n\n${diffString}` : "");
/* istanbul ignore next */ (diffString
? `\n\nDifference:\n\n${diffString}`
: "");
return { pass, message };
}
......