Skip to content

Commit

Permalink
Convert validation tests to jest (#1748)
Browse files Browse the repository at this point in the history
* Convert validation tests to jest

* Rename acceptance files

* Fix duplicate lockfile dependencies

* Fix linters and update script

* Fix test errors except for thinking helper files are tests

* Stricter Jest test matching pattern

* Update CSS tests to return promise

* Fix HTML test promises

* Use babel-plugin-transform-globals for html-inspector

* Fix test filename

* Implement mock createRange in test

* Clean up unused code

* Remove commented code

* Fix karma tests by adding back webpack import loader

* Fix tests
  • Loading branch information
wylieconlon authored and outoftime committed Oct 13, 2019
1 parent 2ab7e36 commit 23782fb
Show file tree
Hide file tree
Showing 38 changed files with 862 additions and 915 deletions.
5 changes: 4 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@
"node": true,
"jest": true
},
"files": ["**/{__factories__,__mocks__,__tests__}/*.js{,x}"],
"files": [
"**/{__factories__,__mocks__,__tests__}/**/*.js{,x}",
"jest-setup.js"
],
"rules": {
"jest/no-alias-methods": "warn",
"jest/no-disabled-tests": "warn",
Expand Down
2 changes: 2 additions & 0 deletions __mocks__/html-inspector/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
jest.requireActual('html-inspector/html-inspector');
export default window.HTMLInspector;
1 change: 1 addition & 0 deletions __mocks__/html-inspector/window.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default {};
6 changes: 6 additions & 0 deletions __mocks__/i18next.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export function t(key) {
if (key === 'utility.or') {
return ' or ';
}
return '';
}
13 changes: 13 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,18 @@ module.exports = api => {
],
plugins,
compact: false,
overrides: isJest
? [
{
test: './node_modules/html-inspector/html-inspector.js',
plugins: [
[
'transform-globals',
{import: {'html-inspector/window': {window: 'default'}}},
],
],
},
]
: [],
};
};
3 changes: 2 additions & 1 deletion config/lintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
/dist
/node_modules
/nodeenv
/test/data
/src/validations/__tests__/acceptance.json
/src/validations/__tests__/acceptance
/templates
43 changes: 43 additions & 0 deletions jest-setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
function createRange() {
// createRange is always called with a Document context
/* eslint-disable no-invalid-this, consistent-this */
const context = this;
let end;
return {
setEndBefore(el) {
end = el;
},

// Finds all text nodes up to the target node, and returns them
// This is used to count newlines for the error reporting
toString() {
let hasFoundNode = false;
const newLines = [];
const iterator = context.createNodeIterator(
context.documentElement,
NodeFilter.SHOW_ALL,
{
acceptNode(node) {
if (hasFoundNode) {
return NodeFilter.FILTER_REJECT;
}
if (node.isEqualNode(end)) {
hasFoundNode = true;
}
if (node.nodeType === Node.TEXT_NODE) {
newLines.push(node.textContent);
}
return NodeFilter.FILTER_ACCEPT;
},
},
);
const nodes = [];
let node;
while ((node = iterator.nextNode())) {
nodes.push(node);
}
return newLines.join('');
},
};
}
global.Document.prototype.createRange = createRange;
4 changes: 3 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ module.exports = {
moduleNameMapper: {
'@factories/(.*)$': '<rootDir>/__factories__/$1',
'\\.(html|svg)': '<rootDir>/__mocks__/fileMock.js',
i18next: '<rootDir>/__mocks__/i18next.js',
},
testMatch: ['**/__tests__/**/*.test.js?(x)'],
testPathIgnorePatterns: ['/node_modules/', '/bower_components/', '/nodeenv/'],
transformIgnorePatterns: ['node_modules/(?!(lodash-es)/)'],
setupFilesAfterEnv: ['jest-extended'],
setupFilesAfterEnv: ['jest-extended', './jest-setup.js'],
};
9 changes: 5 additions & 4 deletions script/update-validation-acceptance
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,24 @@
/* eslint-env node */

const fs = require('fs');

const path = require('path');

const output = {};

['html', 'css', 'javascript'].forEach((language) => {
const dir = `./test/data/acceptance/${language}`;
['html', 'css', 'javascript'].forEach(language => {
const dir = `./src/validations/__tests__/acceptance/${language}`;

output[language] = [];

fs.readdirSync(dir).forEach((file) => {
fs.readdirSync(dir).forEach(file => {
output[language].push(
fs.readFileSync(path.join(dir, file), {encoding: 'utf8'}),
);
});
});

fs.writeFile(
'./test/data/acceptance.json',
'./src/validations/__tests__/acceptance.json',
JSON.stringify(output, null, 2),
);
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
160 changes: 160 additions & 0 deletions src/validations/__tests__/css.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import css from '../css';

import testValidatorAcceptance from './testValidatorAcceptance';
import validationTest from './validationHelper';

describe('css validation', () => {
test('valid flexbox', () =>
validationTest(
`.flex-container {
display: flex;
flex-flow: nowrap column;
align-content: flex-end;
justify-content: flex-start;
align-items: center;
}
.flex-item {
flex: 1 0 auto;
align-self: flex-end;
order: 2;
}`,
css,
));

test('valid filter', () =>
validationTest(
`img {
filter: grayscale(100%);
}`,
css,
));

test('valid text-shadow declaration', () =>
validationTest(
`p {
text-shadow: rgba(0,0,0,0.1) 0 -5px, rgba(0,0,0,0.1) 0 -1px, \
rgba(255,255,255,0.1) 1px 0, rgba(255,255,255,0.1) 0 1px, \
rgba(0,0,0,0.1) -1px -1px, rgba(255,255,255,0.1) 1px 1px;
}`,
css,
));

test('valid background-position declarations', () =>
validationTest(
`img {
background-position-x: 10px;
background-position-y: 15%;
}`,
css,
));

test('bogus flex value', () =>
validationTest('.flex-item { flex: bogus; }', css, {
reason: 'invalid-value',
row: 0,
payload: {error: 'bogus'},
}));

test('fails with bogus filter value', () =>
validationTest('img { filter: whitescale(100%); }', css, {
reason: 'invalid-value',
row: 0,
payload: {error: 'whitescale('},
}));

test('no opening curly brace', () =>
validationTest(
`p
display: block;`,
css,
{reason: 'block-expected', row: 0, payload: {error: 'p'}},
));

test('no closing curly brace', () =>
validationTest(
`p {
display: block;`,
css,
{reason: 'missing-closing-curly', row: 0},
));

test('bogus character in selector', () =>
validationTest('p; div { display: block; }', css, {
reason: 'invalid-token-in-selector',
row: 0,
payload: {token: ';'},
}));

test('invalid negative value', () =>
validationTest('p { padding-left: -2px; }', css, {
reason: 'invalid-negative-value',
row: 0,
payload: {error: '-2px'},
}));

test('invalid fractional value', () =>
validationTest('p { z-index: 2.4; }', css, {
reason: 'invalid-fractional-value',
row: 0,
payload: {error: '2.4'},
}));

test('missing semicolon at end of block', () =>
validationTest(
`p {
display: block
}`,
css,
{reason: 'missing-semicolon', row: 1},
));

test('missing semicolon within block', () =>
validationTest(
`
p {
margin: 10px
padding: 5px;
}
`,
css,
{reason: 'missing-semicolon', row: 2},
));

test('extra tokens after value', () =>
validationTest(
`
p {
padding: 5px 5px 5px 5px 5px;
}
`,
css,
{reason: 'extra-tokens-after-value', row: 2, payload: {token: '5px'}},
));

test('potentially missing semicolon before first line', () =>
validationTest(
`button{border:20px solid b;
}`,
css,
{reason: 'extra-tokens-after-value', row: 0, payload: {token: 'b'}},
));

test('extra token that is prefix of the beginning of the line', () =>
validationTest(
`
p {
border: 20px solid b;
}
`,
css,
{reason: 'extra-tokens-after-value', row: 2, payload: {token: 'b'}},
));

test('thoroughly unparseable CSS', () =>
validationTest('<a href="http;.facebook.com>', css, {
reason: 'invalid-token',
row: 0,
}));

testValidatorAcceptance(css, 'css');
});
Loading

0 comments on commit 23782fb

Please sign in to comment.