mirror of
https://github.com/jlengrand/open-wc.git
synced 2026-03-10 08:31:19 +00:00
feat: add semantic-dom-diff
This commit is contained in:
committed by
Thomas Allmer
parent
123890938c
commit
c7d8c1f832
21
packages/semantic-dom-diff/LICENSE
Normal file
21
packages/semantic-dom-diff/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 open-wc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
9
packages/semantic-dom-diff/README.md
Normal file
9
packages/semantic-dom-diff/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Open Web Component Recommendations to compare dom and shadowDom trees
|
||||
|
||||
> Part of Open Web Component Recommendations [open-wc](https://github.com/open-wc/open-wc/)
|
||||
|
||||
We want to provide a good set of default on how to vasilitate your web component.
|
||||
|
||||
[](https://circleci.com/gh/open-wc/open-wc)
|
||||
[](https://www.browserstack.com/automate/public-build/M2UrSFVRang2OWNuZXlWSlhVc3FUVlJtTDkxMnp6eGFDb2pNakl4bGxnbz0tLUE5RjhCU0NUT1ZWa0NuQ3MySFFWWnc9PQ==--86f7fac07cdbd01dd2b26ae84dc6c8ca49e45b50)
|
||||
[](https://renovatebot.com/)
|
||||
1
packages/semantic-dom-diff/index.js
Normal file
1
packages/semantic-dom-diff/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { getSemanticDomDiff } from './src/get-dom-diff';
|
||||
59
packages/semantic-dom-diff/karma-bs.config.js
Normal file
59
packages/semantic-dom-diff/karma-bs.config.js
Normal file
@@ -0,0 +1,59 @@
|
||||
const originalConfigFn = require('./karma.conf.js');
|
||||
|
||||
let originalConfig;
|
||||
originalConfigFn({ set: (config) => { originalConfig = config; } });
|
||||
|
||||
if (!process.env.BROWSER_STACK_USERNAME || !process.env.BROWSER_STACK_ACCESS_KEY) {
|
||||
throw new Error(`
|
||||
!!You have to set your Browserstack automate username and key!!
|
||||
|
||||
Login and go to https://www.browserstack.com/accounts/settings
|
||||
then run in your console (or add it to your ~/.bashrc)
|
||||
|
||||
export BROWSER_STACK_USERNAME=[username];
|
||||
export BROWSER_STACK_ACCESS_KEY=[key];
|
||||
`);
|
||||
}
|
||||
|
||||
module.exports = (config) => {
|
||||
config.set({
|
||||
...originalConfig,
|
||||
|
||||
browserStack: {
|
||||
username: process.env.BROWSER_STACK_USERNAME,
|
||||
accessKey: process.env.BROWSER_STACK_ACCESS_KEY,
|
||||
project: 'open-wc',
|
||||
},
|
||||
|
||||
browsers: [
|
||||
'bs_win10_chrome_69',
|
||||
'bs_win10_firefox_62',
|
||||
// 'bs_win10_ie_11',
|
||||
],
|
||||
|
||||
// define browsers
|
||||
customLaunchers: {
|
||||
bs_win10_chrome_69: {
|
||||
base: 'BrowserStack',
|
||||
browser: 'Chrome',
|
||||
browser_version: '69.0',
|
||||
os: 'Windows',
|
||||
os_version: '10',
|
||||
},
|
||||
bs_win10_firefox_62: {
|
||||
base: 'BrowserStack',
|
||||
browser: 'Firefox',
|
||||
browser_version: '62.0',
|
||||
os: 'Windows',
|
||||
os_version: '10',
|
||||
},
|
||||
bs_win10_ie_11: {
|
||||
base: 'BrowserStack',
|
||||
browser: 'IE',
|
||||
browser_version: '11.0',
|
||||
os: 'Windows',
|
||||
os_version: '10',
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
91
packages/semantic-dom-diff/karma.conf.js
Normal file
91
packages/semantic-dom-diff/karma.conf.js
Normal file
@@ -0,0 +1,91 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = (config) => {
|
||||
config.set({
|
||||
browsers: [
|
||||
'ChromeHeadlessNoSandbox',
|
||||
// 'FirefoxHeadless'
|
||||
],
|
||||
|
||||
customLaunchers: {
|
||||
ChromeHeadlessNoSandbox: {
|
||||
base: 'ChromeHeadless',
|
||||
flags: [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
],
|
||||
},
|
||||
FirefoxHeadless: {
|
||||
base: 'Firefox',
|
||||
flags: ['-headless'],
|
||||
},
|
||||
},
|
||||
|
||||
frameworks: ['mocha'],
|
||||
files: [
|
||||
{ pattern: '../../node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js', watched: false },
|
||||
{ pattern: '../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js', watched: false },
|
||||
{ pattern: '../../node_modules/chai/chai.js', watched: false },
|
||||
'test/index.js',
|
||||
],
|
||||
preprocessors: {
|
||||
'test/index.js': ['webpack', 'sourcemap'],
|
||||
},
|
||||
webpackMiddleware: {
|
||||
stats: 'errors-only',
|
||||
},
|
||||
reporters: ['dots', 'coverage-istanbul'],
|
||||
colors: true,
|
||||
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN ||
|
||||
// config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_ERROR,
|
||||
|
||||
// ## code coverage config
|
||||
coverageIstanbulReporter: {
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
dir: path.join(__dirname, 'coverage'),
|
||||
combineBrowserReports: true,
|
||||
skipFilesWithNoCoverage: true,
|
||||
thresholds: {
|
||||
global: {
|
||||
statements: 70,
|
||||
lines: 70,
|
||||
branches: 0, // no real test files here
|
||||
functions: 70,
|
||||
},
|
||||
},
|
||||
},
|
||||
webpack: {
|
||||
devtool: 'inline-source-map',
|
||||
mode: 'development',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules\/(?!(@webcomponents\/shadycss|lit-html)\/).*/,
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'istanbul-instrumenter-loader',
|
||||
enforce: 'post',
|
||||
include: path.resolve('./'),
|
||||
exclude: /node_modules|\.test\.js$/,
|
||||
options: {
|
||||
esModules: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// ci settings
|
||||
autoWatch: false,
|
||||
singleRun: true,
|
||||
concurrency: Infinity,
|
||||
});
|
||||
};
|
||||
29
packages/semantic-dom-diff/package.json
Normal file
29
packages/semantic-dom-diff/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@open-wc/semantic-dom-diff",
|
||||
"version": "0.0.0",
|
||||
"description": "To compare dom and shadow dom trees. Part of open-wc recommendations",
|
||||
"author": "open-wc",
|
||||
"homepage": "https://github.com/open-wc/open-wc/",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": "https://github.com/open-wc/open-wc/tree/master/packages/semantic-dom-diff",
|
||||
"scripts": {
|
||||
"test": "karma start",
|
||||
"test:bs": "karma start karma-bs.config.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bundled-es-modules/deep-diff": "^1.0.2-rc.1",
|
||||
"@bundled-es-modules/parse5": "^5.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@open-wc/testing-karma": "^0.1.3",
|
||||
"@open-wc/testing-karma-bs": "^0.0.3",
|
||||
"@open-wc/testing-wallaby": "^0.1.3",
|
||||
"@webcomponents/webcomponentsjs": "^2.0.0",
|
||||
"chai": "^4.0.0",
|
||||
"mocha": "^5.0.0",
|
||||
"sinon": "^7.0.0"
|
||||
}
|
||||
}
|
||||
39
packages/semantic-dom-diff/src/find-diffed-object.js
Normal file
39
packages/semantic-dom-diff/src/find-diffed-object.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import { isParentNode, isElement } from './parse5-utils';
|
||||
|
||||
/**
|
||||
* @param {ASTNode | ASTNode[]} root
|
||||
* @param {string[]} path
|
||||
*/
|
||||
export function findDiffedObject(root, path) {
|
||||
let node = root;
|
||||
|
||||
for (let i = 0; i < path.length; i += 1) {
|
||||
const step = path[i];
|
||||
|
||||
if (Array.isArray(node)) {
|
||||
const intStep = parseFloat(step);
|
||||
if (Number.isInteger(intStep)) {
|
||||
node = node[intStep];
|
||||
} else {
|
||||
throw new Error(`Non-integer step: ${step} for array node.`);
|
||||
}
|
||||
} else if (step === 'childNodes') {
|
||||
if (isParentNode(node)) {
|
||||
node = node.childNodes;
|
||||
} else {
|
||||
throw new Error('Cannot read childNodes from non-parent node.');
|
||||
}
|
||||
} else if (step === 'attrs') {
|
||||
if (isElement(node)) {
|
||||
node = node.attrs;
|
||||
} else {
|
||||
throw new Error('Cannot read attributes from non-element node.');
|
||||
}
|
||||
} else {
|
||||
// For all other steps we don't walk further
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
72
packages/semantic-dom-diff/src/get-diff-message.js
Normal file
72
packages/semantic-dom-diff/src/get-diff-message.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import { isElement, isTextNode } from './parse5-utils';
|
||||
|
||||
export const isAttribute = arg => arg && 'name' in arg && 'value' in arg;
|
||||
const { isArray } = Array;
|
||||
|
||||
/**
|
||||
* @param {ASTNode | Attribute} arg
|
||||
* @returns {string} the human readable diff identifier
|
||||
*/
|
||||
function getIdentifier(arg) {
|
||||
if (isTextNode(arg)) {
|
||||
return `text "${arg.value}"`;
|
||||
}
|
||||
|
||||
if (isElement(arg)) {
|
||||
return `tag <${arg.tagName}>`;
|
||||
}
|
||||
|
||||
if (isAttribute(arg)) {
|
||||
return arg.value
|
||||
? `attribute [${arg.name}="${arg.value}"]`
|
||||
: `attribute [${arg.name}]`;
|
||||
}
|
||||
|
||||
throw new Error(`Unknown arg: ${arg}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the diff is an array diff, returns type assertions to remove optional props.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isArrayDiff(diff) {
|
||||
return diff.kind === 'A' && !!diff.item && typeof diff.index === 'number';
|
||||
}
|
||||
|
||||
const messageTemplates = {
|
||||
// New diff
|
||||
N: (diff, lhs, rhs) => `${getIdentifier(rhs)} has been added`,
|
||||
// Edit diff
|
||||
E: (diff, lhs, rhs) => `${getIdentifier(lhs)} was changed to ${getIdentifier(rhs)}`,
|
||||
// Delete diff
|
||||
D: (diff, lhs) => `${getIdentifier(lhs)} has been removed`,
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a human understandable message for a HTML diff.
|
||||
*
|
||||
* @param {object} diff The diff
|
||||
* @param {object | object[]} lhs The left hand side diffed object.
|
||||
* Can be a HTML ASTNode or an Attribute.
|
||||
* @param {object | object[]} rhs The left hand side diffed object.
|
||||
* Can be a HTML ASTNode or an Attribute.
|
||||
*
|
||||
* @returns the message
|
||||
*/
|
||||
export function getDiffMessage(diff, lhs, rhs) {
|
||||
// Array diff
|
||||
if (isArray(lhs) || isArray(rhs)) {
|
||||
if (!isArrayDiff(diff) || !isArray(lhs) || !isArray(rhs)) {
|
||||
throw new Error('Not all arguments are array diffs');
|
||||
}
|
||||
|
||||
return getDiffMessage(diff.item, lhs[diff.index], rhs[diff.index]);
|
||||
}
|
||||
|
||||
// Non-array diff
|
||||
if (diff.kind in messageTemplates) {
|
||||
return messageTemplates[diff.kind](diff, lhs, rhs);
|
||||
}
|
||||
|
||||
throw new Error(`Unknown diff kind: ${diff.kind}`);
|
||||
}
|
||||
67
packages/semantic-dom-diff/src/get-diff-path.js
Normal file
67
packages/semantic-dom-diff/src/get-diff-path.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import { isParentNode, isElement } from './parse5-utils';
|
||||
|
||||
/**
|
||||
* @param {*} node The AST Node
|
||||
* @returns {string | null} the AST node name
|
||||
*/
|
||||
function getNodeName(node) {
|
||||
if (!isElement(node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (node.attrs) {
|
||||
const idAttr = node.attrs.find(attr => attr.name === 'id');
|
||||
if (idAttr) {
|
||||
return `${node.nodeName}#${idAttr.value}`;
|
||||
}
|
||||
|
||||
const classAttr = node.attrs.find(attr => attr.name === 'class');
|
||||
if (classAttr) {
|
||||
return `${node.nodeName}.${classAttr.value.split(' ').join('.')}`;
|
||||
}
|
||||
}
|
||||
|
||||
return node.nodeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ASTNode | ASTNode[]} root the root to walk from
|
||||
* @param {string[]} path the full path to the dom element
|
||||
* @returns {string[]} the human readable path to a dom element
|
||||
*/
|
||||
export function getDiffPath(root, path) {
|
||||
const names = [];
|
||||
let node = root;
|
||||
|
||||
for (let i = 0; i < path.length; i += 1) {
|
||||
const step = path[i];
|
||||
|
||||
if (Array.isArray(node)) {
|
||||
const intStep = parseFloat(step);
|
||||
if (Number.isInteger(intStep)) {
|
||||
node = node[intStep];
|
||||
} else {
|
||||
throw new Error(`Non-integer step: ${step} for array node.`);
|
||||
}
|
||||
} else if (step === 'childNodes') {
|
||||
if (isParentNode(node)) {
|
||||
node = node.childNodes;
|
||||
} else {
|
||||
throw new Error('Cannot read childNodes from non-parent node.');
|
||||
}
|
||||
} else {
|
||||
// Break loop if we end up at a type of path section we don't want
|
||||
// walk further into
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Array.isArray(node)) {
|
||||
const name = getNodeName(node);
|
||||
if (name) {
|
||||
names.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return names.join(' > ');
|
||||
}
|
||||
67
packages/semantic-dom-diff/src/get-dom-diff.js
Normal file
67
packages/semantic-dom-diff/src/get-dom-diff.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import { parseFragment } from '@bundled-es-modules/parse5';
|
||||
import { deepDiff } from '@bundled-es-modules/deep-diff';
|
||||
|
||||
import { sanitizeHtmlString } from './sanitize-html-string';
|
||||
import { normalizeAST } from './normalize-ast';
|
||||
import { getDiffMessage } from './get-diff-message';
|
||||
import { findDiffedObject } from './find-diffed-object';
|
||||
import { getDiffPath } from './get-diff-path';
|
||||
|
||||
/**
|
||||
* @typedef {object} DiffResult
|
||||
* @param {string} message
|
||||
* @param {string} path
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates the DiffResult for two AST trees.
|
||||
*
|
||||
* @param {ASTNode} leftTree the left tree AST
|
||||
* @param {ASTNode} rightTree the right tree AST
|
||||
* @param {Object} diff the semantic difference between the two trees
|
||||
*
|
||||
* @returns {DiffResult} the diff result containing the human readable semantic difference
|
||||
*/
|
||||
function createDiffResult(leftTree, rightTree, diff) {
|
||||
const leftDiffObject = findDiffedObject(leftTree, diff.path);
|
||||
const rightDiffObject = findDiffedObject(rightTree, diff.path);
|
||||
|
||||
return {
|
||||
message: getDiffMessage(diff, leftDiffObject, rightDiffObject),
|
||||
path: getDiffPath(leftTree, diff.path),
|
||||
};
|
||||
}
|
||||
|
||||
export function getAST(value, config = {}) {
|
||||
const ast = parseFragment(sanitizeHtmlString(value));
|
||||
normalizeAST(ast, config.ignoredTags);
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses two HTML trees, and generates the semantic difference between the two trees.
|
||||
* The HTML is diffed semantically, not literally. This means that changes in attribute
|
||||
* and class order and whitespace/newlines are ignored. Also, script and style
|
||||
* tags ignored.
|
||||
*
|
||||
* @param leftHTML the left HTML tree
|
||||
* @param rightHTML the right HTML tree
|
||||
* @returns {DiffResult | null} the diff result, or undefined if no diffs were found
|
||||
*/
|
||||
export function getSemanticDomDiff(leftHTML, rightHTML, config = {}) {
|
||||
const leftTree = getAST(leftHTML);
|
||||
const rightTree = getAST(rightHTML);
|
||||
|
||||
normalizeAST(leftTree, config.ignoredTags);
|
||||
normalizeAST(rightTree, config.ignoredTags);
|
||||
|
||||
// parentNode causes a circular reference, so ignore them.
|
||||
const ignore = (path, key) => key === 'parentNode';
|
||||
const diffs = deepDiff(leftTree, rightTree, ignore);
|
||||
|
||||
if (!diffs || !diffs.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return createDiffResult(leftTree, rightTree, diffs[0]);
|
||||
}
|
||||
52
packages/semantic-dom-diff/src/normalize-ast.js
Normal file
52
packages/semantic-dom-diff/src/normalize-ast.js
Normal file
@@ -0,0 +1,52 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { isElement, isParentNode } from './parse5-utils';
|
||||
|
||||
const defaultIgnoresTags = ['style', 'script', '#comment'];
|
||||
|
||||
function filterNode(node, ignoredTags) {
|
||||
return !defaultIgnoresTags.includes(node.nodeName) && !ignoredTags.includes(node.nodeName);
|
||||
}
|
||||
|
||||
function sortAttributes(attrs) {
|
||||
return attrs
|
||||
// Sort attributes
|
||||
.map((attr) => {
|
||||
if (attr.name === 'class') {
|
||||
attr.value = attr.value.trim().split(/\s+/).sort().join(' ');
|
||||
}
|
||||
|
||||
return attr;
|
||||
})
|
||||
// Sort classes
|
||||
.sort((attrA, attrB) => {
|
||||
const a = attrA.name.toLowerCase();
|
||||
const b = attrB.name.toLowerCase();
|
||||
|
||||
if (a < b) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a > b) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalized AST tree, normlaizing whitespace, attribute + class order etc. Not a pure function,
|
||||
* mutates input.
|
||||
* @param {ASTNode} node
|
||||
* @param {string[]} ignoredTags
|
||||
*/
|
||||
export function normalizeAST(node, ignoredTags = []) {
|
||||
if (isElement(node)) {
|
||||
node.attrs = sortAttributes(node.attrs);
|
||||
}
|
||||
|
||||
if (isParentNode(node)) {
|
||||
node.childNodes = node.childNodes.filter(child => filterNode(child, ignoredTags));
|
||||
node.childNodes.forEach(child => normalizeAST(child, ignoredTags));
|
||||
}
|
||||
}
|
||||
6
packages/semantic-dom-diff/src/parse5-utils.js
Normal file
6
packages/semantic-dom-diff/src/parse5-utils.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export const isNode = arg => arg && 'nodeName' in arg;
|
||||
export const isElement = arg => arg && 'tagName' in arg;
|
||||
export const isParentNode = arg => arg && 'childNodes' in arg;
|
||||
export const isTextNode = arg => arg && arg.nodeName === '#text';
|
||||
export const isCommentNode = arg => arg && arg.nodeName === '#comment';
|
||||
export const isDocumentFragment = arg => arg && arg.nodeName === '#document-fragment';
|
||||
11
packages/semantic-dom-diff/src/sanitize-html-string.js
Normal file
11
packages/semantic-dom-diff/src/sanitize-html-string.js
Normal file
@@ -0,0 +1,11 @@
|
||||
export function sanitizeHtmlString(htmlString) {
|
||||
return htmlString
|
||||
// Remove space characters after opening tags
|
||||
.replace(/>\s+/g, '>')
|
||||
// Remove space characters before closing tags
|
||||
.replace(/\s+</g, '<')
|
||||
// remove lit-html expression markers
|
||||
.replace(/<!---->/g, '')
|
||||
// Remove leading and trailing whitespace
|
||||
.trim();
|
||||
}
|
||||
2
packages/semantic-dom-diff/test/bdd-setup.js
Normal file
2
packages/semantic-dom-diff/test/bdd-setup.js
Normal file
@@ -0,0 +1,2 @@
|
||||
// register-expect
|
||||
window.expect = window.chai.expect;
|
||||
24
packages/semantic-dom-diff/test/index.html
Normal file
24
packages/semantic-dom-diff/test/index.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link href="../../../mocha/mocha.css" rel="stylesheet" />
|
||||
<script src="../../../mocha/mocha.js"></script>
|
||||
<script src="../../../chai/chai.js"></script>
|
||||
<script src="../../../sinon/pkg/sinon.js"></script>
|
||||
<script src="../../../@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
<script>
|
||||
mocha.setup('bdd');
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
import './index.js';
|
||||
|
||||
mocha.checkLeaks();
|
||||
mocha.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
4
packages/semantic-dom-diff/test/index.js
Normal file
4
packages/semantic-dom-diff/test/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
// do manual setup and not use testing to not have circle dependencies
|
||||
import './bdd-setup.js';
|
||||
|
||||
import './semantic-dom-diff.test.js';
|
||||
157
packages/semantic-dom-diff/test/large-template.js
Normal file
157
packages/semantic-dom-diff/test/large-template.js
Normal file
@@ -0,0 +1,157 @@
|
||||
export default `
|
||||
<section>
|
||||
<div>
|
||||
<div>
|
||||
<h2>What is Lorem Ipsum?</h2>
|
||||
<p>
|
||||
<strong>Lorem Ipsum</strong> is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's
|
||||
standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make
|
||||
a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining
|
||||
essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum
|
||||
passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Why do we use it?</h2>
|
||||
<p>It is a long established fact that a reader will be distracted by the readable content of a page when looking at its
|
||||
layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to
|
||||
using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web
|
||||
page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web
|
||||
sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose
|
||||
(injected humour and the like).</p>
|
||||
</div>
|
||||
<br>
|
||||
<div>
|
||||
<h2>Where does it come from?</h2>
|
||||
<p>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature
|
||||
from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia,
|
||||
looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites
|
||||
of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and
|
||||
1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book
|
||||
is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem
|
||||
ipsum dolor sit amet..", comes from a line in section 1.10.32.</p>
|
||||
<p>The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and
|
||||
1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied
|
||||
by English versions from the 1914 translation by H. Rackham.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Where can I get some?</h2>
|
||||
<p>There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form,
|
||||
by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage
|
||||
of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem
|
||||
Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator
|
||||
on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures,
|
||||
to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition,
|
||||
injected humour, or non-characteristic words etc.</p>
|
||||
<form method="post" action="/feed/html">
|
||||
<table style="width:100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="2">
|
||||
<input type="text" name="amount" value="5" size="3" id="amount">
|
||||
</td>
|
||||
<td rowspan="2">
|
||||
<table style="text-align:left">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width:20px">
|
||||
<input type="radio" name="what" value="paras" id="paras" checked="checked">
|
||||
</td>
|
||||
<td>
|
||||
<label for="paras">paragraphs</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20px">
|
||||
<input type="radio" name="what" value="words" id="words">
|
||||
</td>
|
||||
<td>
|
||||
<label for="words">words</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20px">
|
||||
<input type="radio" name="what" value="bytes" id="bytes">
|
||||
</td>
|
||||
<td>
|
||||
<label for="bytes">bytes</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:20px">
|
||||
<input type="radio" name="what" value="lists" id="lists">
|
||||
</td>
|
||||
<td>
|
||||
<label for="lists">lists</label>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td style="width:20px">
|
||||
<input type="checkbox" name="start" id="start" value="yes" checked="checked">
|
||||
</td>
|
||||
<td style="text-align:left">
|
||||
<label for="start">Start with 'Lorem
|
||||
<br>ipsum dolor sit amet...'</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td style="text-align:left">
|
||||
<input type="submit" name="generate" id="generate" value="Generate Lorem Ipsum">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="Translation">
|
||||
|
||||
<h3>The standard Lorem Ipsum passage, used since the 1500s</h3>
|
||||
<p>"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
|
||||
occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."</p>
|
||||
<h3>Section 1.10.32 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC</h3>
|
||||
<p>"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam,
|
||||
eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam
|
||||
voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem
|
||||
sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia
|
||||
non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam,
|
||||
quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem
|
||||
vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum
|
||||
fugiat quo voluptas nulla pariatur?"</p>
|
||||
<h3>1914 translation by H. Rackham</h3>
|
||||
<p>"But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give
|
||||
you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder
|
||||
of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those
|
||||
who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there
|
||||
anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances
|
||||
occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes
|
||||
laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man
|
||||
who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant
|
||||
pleasure?"</p>
|
||||
<h3>Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC</h3>
|
||||
<p>"At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti
|
||||
quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia
|
||||
deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio.
|
||||
Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere
|
||||
possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis
|
||||
aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum
|
||||
rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus
|
||||
asperiores repellat."</p>
|
||||
<h3>1914 translation by H. Rackham</h3>
|
||||
<p>"On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the
|
||||
charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound
|
||||
to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying
|
||||
through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when
|
||||
our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure
|
||||
is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations
|
||||
of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore
|
||||
always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures,
|
||||
or else he endures pains to avoid worse pains."</p>
|
||||
</div>
|
||||
</section>
|
||||
`;
|
||||
337
packages/semantic-dom-diff/test/semantic-dom-diff.test.js
Normal file
337
packages/semantic-dom-diff/test/semantic-dom-diff.test.js
Normal file
@@ -0,0 +1,337 @@
|
||||
import { getSemanticDomDiff } from '../index.js';
|
||||
import largeTemplate from './large-template';
|
||||
|
||||
describe('getSemanticDomDiff()', () => {
|
||||
describe('diffs', () => {
|
||||
describe('element', () => {
|
||||
it('changed element', () => {
|
||||
const diff = getSemanticDomDiff('<div></div>', '<span></span>');
|
||||
|
||||
expect(diff.message).to.equal('tag <div> was changed to tag <span>');
|
||||
expect(diff.path).to.equal('div');
|
||||
});
|
||||
|
||||
it('added element', () => {
|
||||
const diff = getSemanticDomDiff('<div></div>', '<div></div><div></div>');
|
||||
|
||||
expect(diff.message).to.equal('tag <div> has been added');
|
||||
expect(diff.path).to.equal('');
|
||||
});
|
||||
|
||||
it('removed element', () => {
|
||||
const diff = getSemanticDomDiff('<div></div><div></div>', '<div></div>');
|
||||
|
||||
expect(diff.message).to.equal('tag <div> has been removed');
|
||||
expect(diff.path).to.equal('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('attributes', () => {
|
||||
it('changed attribute', () => {
|
||||
const diff = getSemanticDomDiff('<div foo="bar"></div>', '<div foo="baz"></div>');
|
||||
|
||||
expect(diff.message).to.equal('attribute [foo="bar"] was changed to attribute [foo="baz"]');
|
||||
expect(diff.path).to.equal('div');
|
||||
});
|
||||
|
||||
it('added attribute', () => {
|
||||
const diff = getSemanticDomDiff('<div></div>', '<div foo="bar"></div>');
|
||||
|
||||
expect(diff.message).to.equal('attribute [foo="bar"] has been added');
|
||||
expect(diff.path).to.equal('div');
|
||||
});
|
||||
|
||||
it('removed attribute', () => {
|
||||
const diff = getSemanticDomDiff('<div foo="bar"></div>', '<div></div>');
|
||||
|
||||
expect(diff.message).to.equal('attribute [foo="bar"] has been removed');
|
||||
expect(diff.path).to.equal('div');
|
||||
});
|
||||
});
|
||||
|
||||
describe('text', () => {
|
||||
it('changed text', () => {
|
||||
const diff = getSemanticDomDiff('<div>foo</div>', '<div>bar</div>');
|
||||
|
||||
expect(diff.message).to.equal('text "foo" was changed to text "bar"');
|
||||
expect(diff.path).to.equal('div');
|
||||
});
|
||||
|
||||
it('removed text', () => {
|
||||
const diff = getSemanticDomDiff('<div>foo</div>', '<div></div>');
|
||||
|
||||
expect(diff.message).to.equal('text "foo" has been removed');
|
||||
expect(diff.path).to.equal('div');
|
||||
});
|
||||
|
||||
it('added text', () => {
|
||||
const diff = getSemanticDomDiff('<div></div>', '<div>foo</div>');
|
||||
|
||||
expect(diff.message).to.equal('text "foo" has been added');
|
||||
expect(diff.path).to.equal('div');
|
||||
});
|
||||
});
|
||||
|
||||
describe('multiple diffs', () => {
|
||||
it('returns the first diff', () => {
|
||||
const diff = getSemanticDomDiff('<div>foo</div><div foo="bar"></div>', '<div>bar</div><span foo="baz"></span>');
|
||||
|
||||
expect(diff.message).to.equal('tag <div> was changed to tag <span>');
|
||||
expect(diff.path).to.equal('div');
|
||||
});
|
||||
});
|
||||
|
||||
describe('deep changes', () => {
|
||||
it('element changes', () => {
|
||||
const a = `
|
||||
<div>
|
||||
<div id="foo">
|
||||
<div>
|
||||
<div class="foo">
|
||||
<div class="foo bar baz">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
`;
|
||||
const b = `
|
||||
<div>
|
||||
<div id="foo">
|
||||
<div>
|
||||
<div class="foo">
|
||||
<span class="foo bar baz">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
`;
|
||||
const diff = getSemanticDomDiff(a, b);
|
||||
|
||||
expect(diff.message).to.equal('tag <div> was changed to tag <span>');
|
||||
expect(diff.path).to.equal('div > div#foo > div > div.foo > div.bar.baz.foo');
|
||||
});
|
||||
|
||||
it('attribute changes', () => {
|
||||
const a = `
|
||||
<div>
|
||||
<div id="foo">
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
`;
|
||||
const b = `
|
||||
<div>
|
||||
<div id="foo">
|
||||
<div>
|
||||
<div foo="bar">
|
||||
<div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
`;
|
||||
const diff = getSemanticDomDiff(a, b);
|
||||
|
||||
expect(diff.message).to.equal('attribute [foo="bar"] has been added');
|
||||
expect(diff.path).to.equal('div > div#foo > div > div');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('equality', () => {
|
||||
describe('simple', () => {
|
||||
it('element', () => {
|
||||
const diff = getSemanticDomDiff('<div></div>', '<div></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('attribute', () => {
|
||||
const diff = getSemanticDomDiff('<div foo="bar"></div>', '<div foo="bar"></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('text', () => {
|
||||
const diff = getSemanticDomDiff('<div>foo</div>', '<div>foo</div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('complex', () => {
|
||||
it('large template', () => {
|
||||
const diff = getSemanticDomDiff(largeTemplate, largeTemplate);
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('self closing tags', () => {
|
||||
const diff = getSemanticDomDiff('<div><br><hr /></div>', '<div><br /><hr></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ordering', () => {
|
||||
it('attributes order', () => {
|
||||
const diff = getSemanticDomDiff('<div a="1" b="2" c="3"></div>', '<div c="3" a="1" b="2"></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('class order', () => {
|
||||
const diff = getSemanticDomDiff('<div class="foo bar"></div>', '<div class="bar foo"></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('whitespace', () => {
|
||||
it('trailing whitespace in attributes', () => {
|
||||
const diff = getSemanticDomDiff('<div foo="bar" ></div>', '<div foo="bar"></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('trailing whitespace in class', () => {
|
||||
const diff = getSemanticDomDiff('<div class="foo bar "></div>', '<div class="foo bar "></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('whitespace between classes', () => {
|
||||
const diff = getSemanticDomDiff('<div class="foo bar "></div>', '<div class="foo bar"></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('whitespace before and after template', () => {
|
||||
const diff = getSemanticDomDiff(' <div></div> ', '<div></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('whitespace in between nodes', () => {
|
||||
const diff = getSemanticDomDiff('<div> </div> foo <div> </div>', '<div></div>foo<div></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('whitespace around text nodes', () => {
|
||||
const diff = getSemanticDomDiff('<div>foo</div>', '<div> foo </div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tabs', () => {
|
||||
it('tabs before and after template', () => {
|
||||
const diff = getSemanticDomDiff('\t\t<div></div>\t', '<div></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('tabs in between nodes', () => {
|
||||
const diff = getSemanticDomDiff('<div>\t<div></div>\t \t \t</div>', '<div><div></div></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('tabs around text nodes', () => {
|
||||
const diff = getSemanticDomDiff('<div>foo</div>', '<div>\tfoo\t</div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('newlines', () => {
|
||||
it('newlines before and after template', () => {
|
||||
const diff = getSemanticDomDiff('\n\n<div></div>\n', '<div></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('newlines in between nodes', () => {
|
||||
const diff = getSemanticDomDiff('<div>\n<div></div>\n \n \n</div>', '<div><div></div></div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('newlines around text nodes', () => {
|
||||
const diff = getSemanticDomDiff('<div>foo</div>', '<div>\n\n\nfoo\n</div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('filtered nodes', () => {
|
||||
it('comments', () => {
|
||||
const diff = getSemanticDomDiff('<div>foo<!-- comment --></div>', '<div>foo</div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('styles', () => {
|
||||
const diff = getSemanticDomDiff('<div>foo<style> .foo { color: blue; } </style></div>', '<div>foo</div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('script', () => {
|
||||
const diff = getSemanticDomDiff('<div>foo<script>console.log("foo");</script></div>', '<div>foo</div>');
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
|
||||
it('ignored tags', () => {
|
||||
const diff = getSemanticDomDiff('<div><span>foo</span></div>', '<div><span>bar</span></div>', { ignoredTags: ['span'] });
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('template string', () => {
|
||||
it('differently formatted', () => {
|
||||
const a = `
|
||||
<div>foo</div>
|
||||
<div>bar</div>
|
||||
<div></div>
|
||||
`;
|
||||
const b = `
|
||||
<div>foo</div>
|
||||
<div>
|
||||
|
||||
|
||||
bar
|
||||
</div>
|
||||
<div></div>
|
||||
`;
|
||||
const diff = getSemanticDomDiff(a, b);
|
||||
|
||||
expect(diff).to.equal(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('values', () => {
|
||||
it('handles strings', () => {
|
||||
const diff = getSemanticDomDiff('<div></div>', '<span></span>');
|
||||
|
||||
expect(diff.message).to.equal('tag <div> was changed to tag <span>');
|
||||
});
|
||||
});
|
||||
});
|
||||
41
packages/semantic-dom-diff/wallaby.js
Normal file
41
packages/semantic-dom-diff/wallaby.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const wallabyWebpack = require('wallaby-webpack'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const path = require('path');
|
||||
|
||||
const wallabyPostprocessor = wallabyWebpack({
|
||||
resolve: {
|
||||
modules: [path.resolve(__dirname, 'bower_components'), 'node_modules'],
|
||||
},
|
||||
entryPatterns: [
|
||||
'test/index.js',
|
||||
],
|
||||
});
|
||||
|
||||
module.exports = () => ({
|
||||
files: [
|
||||
{ pattern: '*.js', load: false },
|
||||
'!wallaby.js',
|
||||
'!*.config.js',
|
||||
'!*.conf.js',
|
||||
{ pattern: 'test/index.js', load: false },
|
||||
{ pattern: 'test/*.test.js', ignore: true },
|
||||
|
||||
{ pattern: 'node_modules/chai/chai.js', instrument: false },
|
||||
{ pattern: 'node_modules/sinon/pkg/sinon.js', instrument: false },
|
||||
],
|
||||
tests: [
|
||||
{ pattern: 'test/*.test.js', load: false },
|
||||
],
|
||||
testFramework: 'mocha',
|
||||
debug: true,
|
||||
env: {
|
||||
kind: 'chrome',
|
||||
params: {
|
||||
runner: '--no-sandbox --disable-setuid-sandbox --headless --disable-gpu --disable-translate --disable-extensions --disable-background-networking --safebrowsing-disable-auto-update --disable-sync --metrics-recording-only --disable-default-apps --no-first-run',
|
||||
},
|
||||
},
|
||||
postprocessor: wallabyPostprocessor,
|
||||
setup: () => {
|
||||
// required to trigger test loading
|
||||
window.__moduleBundler.loadTests();
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user