diff --git a/packages/chai-dom-equals/README.md b/packages/chai-dom-equals/README.md index 79f951e0..59859cb7 100644 --- a/packages/chai-dom-equals/README.md +++ b/packages/chai-dom-equals/README.md @@ -30,8 +30,7 @@ chai.use(chaiDomEquals); ```js it('has the following dom', async () => { const el = await fixture(`

${'Hey'}

`); - expect(el).dom.to.equal('

Hey

'); - expect(el).dom.to.semantically.equal('

Hey

'); + expect(el).dom.to.equal('

Hey

'); }); ``` @@ -49,7 +48,35 @@ it('has the following shadow dom', async () => { } }); const el = await fixture(`<${tag}>`); - expect(el).shadowDom.to.equal('

shadow content

'); - expect(el).shadowDom.to.semantically.equal('

shadow content

'); + expect(el).shadowDom.to.equal('

shadow content

'); }); ``` + +## Literal matching +By default dom is diffed 'semantically'. Differences in whitespace, newlines, attributes/class order are ignored and style, script and commend nodes are removed. + +If you want to match literally instead you can use some of the provided utilities to handle diffing on browsers with the shadow dom polyfill: + +```javascript +import { getOuterHtml, getCleanedShadowDom } from '@open-wc/chai-dom-equals'; + +it('literally equals', () => { + const tag = defineCE(class extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + } + + connectedCallback() { + this.shadowRoot.innerHTML = '

shadow content

'; + } + }); + const el = await fixture(`<${tag}>`); + const outerHTML = getOuterHtml(el); + const innerHTML = getCleanedShadowDom(el); + + expect(outerHTML).to.equal(`<${tag}>`); + expect(innerHTML).to.equal('

shadow content

'); +}); + +``` \ No newline at end of file diff --git a/packages/chai-dom-equals/chai-dom-equals.js b/packages/chai-dom-equals/chai-dom-equals.js index 639e209d..bcec5d07 100644 --- a/packages/chai-dom-equals/chai-dom-equals.js +++ b/packages/chai-dom-equals/chai-dom-equals.js @@ -1,4 +1,4 @@ -import { getSemanticDomDiff } from '@open-wc/semantic-dom-diff'; +import { getDiffableSemanticHTML } from '@open-wc/semantic-dom-diff'; /** * el.outerHTML is not polyfilled so we need to recreate the tag + attributes and @@ -50,6 +50,8 @@ export const chaiDomEquals = (chai, utils) => { }); // can not be an arrow function as it gets rebound + // TODO: this is here for backwards compatibility, removal will be + // a breaking change chai.Assertion.addProperty('semantically', function shadowDom() { new chai.Assertion(this._obj.nodeType).to.equal(1); utils.flag(this, 'semantically', true); @@ -58,60 +60,34 @@ export const chaiDomEquals = (chai, utils) => { // can not be an arrow function as it gets rebound // prettier-ignore const domEquals = _super => function handleDom(value, ...args) { - if (!utils.flag(this, 'semantically') && utils.flag(this, 'dom')) { - const expected = getOuterHtml(this._obj); - this.assert( - value === expected, - 'expected dom #{exp} to equal #{act}', - 'expected dom #{exp} to not equal #{act}', - expected, - value, - ); - } else if (!utils.flag(this, 'semantically') && utils.flag(this, 'shadowDom')) { - const expected = getCleanedShadowDom(this._obj); - this.assert( - value === expected, - 'expected shadow dom #{exp} to equal #{act}', - 'expected shadow dom #{exp} to not equal #{act}', - expected, - value, - ); - } else if (utils.flag(this, 'semantically') && utils.flag(this, 'dom')) { - const result = getSemanticDomDiff(value, getOuterHtml(this._obj)); - const message = result ? result.message : ''; - const path = result ? result.path : ''; - const normalizedHTML = result ? result.normalizedRightHTML : ''; - this.assert( - result === null, - () => { - /* eslint-disable no-console */ - console.log('Snapshot changed, want to accept the change?'); - console.log('Updated Snapshot:'); - console.log(''); - console.log(normalizedHTML); - /* eslint-enable no-console */ - return `expected dom to be semantically equal\n- diff found: ${message}\n- in path: ${path}`; - }, - 'expected dom to not be semantically equal', - ); - } else if (utils.flag(this, 'semantically') && utils.flag(this, 'shadowDom')) { - const result = getSemanticDomDiff(value, getCleanedShadowDom(this._obj)); - const message = result ? result.message : ''; - const path = result ? result.path : ''; - const normalizedHTML = result ? result.normalizedRightHTML : ''; - this.assert( - result === null, - () => { - /* eslint-disable no-console */ - console.log('Snapshot changed, want to accept the change?'); - console.log('Updated Snapshot:'); - console.log(''); - console.log(normalizedHTML); - /* eslint-enable no-console */ - return `expected shadow dom to be semantically equal\n- diff found: ${message}\n- in path: ${path}`; - }, - 'expected shadow dom to not be semantically equal', - ); + if (utils.flag(this, 'dom')) { + const expectedHTML = getDiffableSemanticHTML(value); + const actualHTML = getDiffableSemanticHTML(getOuterHtml(this._obj)); + + // use chai's built-in string comparison, log the updated snapshot on error + try { + new chai.Assertion(actualHTML).to.equal(expectedHTML); + } catch (error) { + console.log('Snapshot changed, want to accept the change:'); + console.log(''); + console.log(actualHTML); + throw error; + } + + } else if (utils.flag(this, 'shadowDom')) { + const expectedHTML = getDiffableSemanticHTML(value); + const actualHTML = getDiffableSemanticHTML(getCleanedShadowDom(this._obj)); + + // use chai's built-in string comparison, log the updated snapshot on error + try { + new chai.Assertion(actualHTML).to.equal(expectedHTML); + } catch (error) { + console.log('Snapshot changed, want to accept the change:'); + console.log(''); + console.log(actualHTML); + throw error; + } + } else { _super.apply(this, [value, ...args]); } diff --git a/packages/chai-dom-equals/test/chai-dom-equals.test.js b/packages/chai-dom-equals/test/chai-dom-equals.test.js index 9ba2a3a3..174691e5 100644 --- a/packages/chai-dom-equals/test/chai-dom-equals.test.js +++ b/packages/chai-dom-equals/test/chai-dom-equals.test.js @@ -152,19 +152,19 @@ describe('getCleanedShadowDom', () => { }); describe('dom', () => { - it('can strictly compare dom nodes', async () => { + it('can compare dom nodes', async () => { const el = await fixture(`

${'Hey'}

`); - expect(el).dom.to.equal('

Hey

'); + expect(el).dom.to.equal('

Hey

'); }); - it('can semmantically compare dom nodes', async () => { + it('handles .semantically as backwards compatibility', async () => { const el = await fixture(`

${'Hey'}

`); expect(el).dom.to.semantically.equal('

Hey

'); }); }); describe('shadowDom', () => { - it('can strict compare shadow dom nodes', async () => { + it('can compare shadow dom nodes', async () => { const tag = defineCE( class extends HTMLElement { constructor() { @@ -178,12 +178,11 @@ describe('shadowDom', () => { }, ); const el = await fixture(`<${tag}> light content `); - expect(el).dom.to.semantically.equal(`<${tag}>light content`); - expect(el).shadowDom.to.equal('

shadow content

'); - expect(el).shadowDom.to.semantically.equal('

shadow content

'); + expect(el).dom.to.equal(`<${tag}>light content`); + expect(el).shadowDom.to.equal('

shadow content

'); }); - it('can semmantically compare shadow dom nodes', async () => { + it('supports .semantically as backwards compatibility', async () => { const tag = defineCE( class extends HTMLElement { constructor() { diff --git a/packages/semantic-dom-diff/index.d.ts b/packages/semantic-dom-diff/index.d.ts index b583063d..6171b594 100644 --- a/packages/semantic-dom-diff/index.d.ts +++ b/packages/semantic-dom-diff/index.d.ts @@ -1 +1 @@ -export { getSemanticDomDiff, getAST } from './src/get-dom-diff'; +export { getDiffableSemanticHTML, getAST } from './src/get-dom-diff'; diff --git a/packages/semantic-dom-diff/index.js b/packages/semantic-dom-diff/index.js index 49f1c3bf..fe35fa5a 100644 --- a/packages/semantic-dom-diff/index.js +++ b/packages/semantic-dom-diff/index.js @@ -1 +1 @@ -export { getSemanticDomDiff } from './src/get-dom-diff.js'; +export { getDiffableSemanticHTML } from './src/get-dom-diff.js'; diff --git a/packages/semantic-dom-diff/package.json b/packages/semantic-dom-diff/package.json index 45268697..11b71e25 100644 --- a/packages/semantic-dom-diff/package.json +++ b/packages/semantic-dom-diff/package.json @@ -17,9 +17,9 @@ "test:es5:watch": "karma start karma.es5.config.js --auto-watch=true --single-run=false" }, "dependencies": { - "@bundled-es-modules/deep-diff": "^1.0.2-rc.1", "@bundled-es-modules/parse5": "^5.1.0", - "@types/parse5": "^5.0.0" + "@types/parse5": "^5.0.0", + "diffable-html": "^3.0.0" }, "devDependencies": { "@bundled-es-modules/chai": "^4.2.0", diff --git a/packages/semantic-dom-diff/src/find-diffed-object.js b/packages/semantic-dom-diff/src/find-diffed-object.js deleted file mode 100644 index 30a0c071..00000000 --- a/packages/semantic-dom-diff/src/find-diffed-object.js +++ /dev/null @@ -1,39 +0,0 @@ -import { isParentNode, isElement } from './parse5-utils.js'; - -/** - * @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; -} diff --git a/packages/semantic-dom-diff/src/get-diff-message.js b/packages/semantic-dom-diff/src/get-diff-message.js deleted file mode 100644 index 6dc1ca5e..00000000 --- a/packages/semantic-dom-diff/src/get-diff-message.js +++ /dev/null @@ -1,70 +0,0 @@ -import { isElement, isTextNode } from './parse5-utils.js'; - -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}`); -} diff --git a/packages/semantic-dom-diff/src/get-diff-path.js b/packages/semantic-dom-diff/src/get-diff-path.js deleted file mode 100644 index d3167b71..00000000 --- a/packages/semantic-dom-diff/src/get-diff-path.js +++ /dev/null @@ -1,67 +0,0 @@ -import { isParentNode, isElement } from './parse5-utils.js'; - -/** - * @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(' > '); -} diff --git a/packages/semantic-dom-diff/src/get-dom-diff.d.ts b/packages/semantic-dom-diff/src/get-dom-diff.d.ts index 674ee923..814ecefb 100644 --- a/packages/semantic-dom-diff/src/get-dom-diff.d.ts +++ b/packages/semantic-dom-diff/src/get-dom-diff.d.ts @@ -1,11 +1,4 @@ import { DocumentFragment } from 'parse5'; -interface DiffResult { - message: String; - path: String; - normalizedLeftHTML: String; - normalizedRightHTML: String; -} - export function getAST(value, config): DocumentFragment -export function getSemanticDomDiff(leftHTML, rightHTML, config): DiffResult +export function getDiffableSemanticHTML(html: string): string diff --git a/packages/semantic-dom-diff/src/get-dom-diff.js b/packages/semantic-dom-diff/src/get-dom-diff.js index 930e49e5..556a9ec8 100644 --- a/packages/semantic-dom-diff/src/get-dom-diff.js +++ b/packages/semantic-dom-diff/src/get-dom-diff.js @@ -1,38 +1,8 @@ import { parseFragment, serialize } from '@bundled-es-modules/parse5'; -import { deepDiff } from '@bundled-es-modules/deep-diff'; +import toDiffableHtml from 'diffable-html'; import { sanitizeHtmlString } from './sanitize-html-string.js'; import { normalizeAST } from './normalize-ast.js'; -import { getDiffMessage } from './get-diff-message.js'; -import { findDiffedObject } from './find-diffed-object.js'; -import { getDiffPath } from './get-diff-path.js'; - -/** - * @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), - normalizedLeftHTML: serialize(leftTree), - normalizedRightHTML: serialize(rightTree), - }; -} export function getAST(value, config = {}) { const ast = parseFragment(sanitizeHtmlString(value)); @@ -41,29 +11,18 @@ export function getAST(value, config = {}) { } /** - * 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. + * Parses HTML and returns HTML in a formatted and diffable format which is semantically + * equal to the input, but with some transformations: + * - whitespace and newlines in and around tags are normalized + * - classes and attributes are ordered + * - script, style and comments are removed * - * @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 + * @param {string} html + * @returns {string} */ -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]); +export function getDiffableSemanticHTML(html) { + const ast = getAST(html); + normalizeAST(ast, []); + const serialized = serialize(ast); + return toDiffableHtml(serialized); } diff --git a/packages/semantic-dom-diff/src/parse5-utils.js b/packages/semantic-dom-diff/src/parse5-utils.js index 9b778f2b..fb424308 100644 --- a/packages/semantic-dom-diff/src/parse5-utils.js +++ b/packages/semantic-dom-diff/src/parse5-utils.js @@ -1,6 +1,2 @@ -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'; diff --git a/packages/semantic-dom-diff/test/semantic-dom-diff.test.js b/packages/semantic-dom-diff/test/semantic-dom-diff.test.js index ed899eaf..6844412f 100644 --- a/packages/semantic-dom-diff/test/semantic-dom-diff.test.js +++ b/packages/semantic-dom-diff/test/semantic-dom-diff.test.js @@ -1,93 +1,81 @@ import { expect } from '@bundled-es-modules/chai'; -import { getSemanticDomDiff } from '../index.js'; +import { getDiffableSemanticHTML } from '../index.js'; import largeTemplate from './large-template'; describe('getSemanticDomDiff()', () => { describe('diffs', () => { describe('element', () => { it('changed element', () => { - const diff = getSemanticDomDiff('
', ''); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML(''); - expect(diff.message).to.equal('tag
was changed to tag '); - expect(diff.path).to.equal('div'); + expect(htmlA).to.not.equal(htmlB); }); it('added element', () => { - const diff = getSemanticDomDiff('
', '
'); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
'); - expect(diff.message).to.equal('tag
has been added'); - expect(diff.path).to.equal(''); + expect(htmlA).to.not.equal(htmlB); }); it('removed element', () => { - const diff = getSemanticDomDiff('
', '
'); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
'); - expect(diff.message).to.equal('tag
has been removed'); - expect(diff.path).to.equal(''); + expect(htmlA).to.not.equal(htmlB); }); }); describe('attributes', () => { it('changed attribute', () => { - const diff = getSemanticDomDiff('
', '
'); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
'); - expect(diff.message).to.equal('attribute [foo="bar"] was changed to attribute [foo="baz"]'); - expect(diff.path).to.equal('div'); + expect(htmlA).to.not.equal(htmlB); }); it('added attribute', () => { - const diff = getSemanticDomDiff('
', '
'); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
'); - expect(diff.message).to.equal('attribute [foo="bar"] has been added'); - expect(diff.path).to.equal('div'); + expect(htmlA).to.not.equal(htmlB); }); it('removed attribute', () => { - const diff = getSemanticDomDiff('
', '
'); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
'); - expect(diff.message).to.equal('attribute [foo="bar"] has been removed'); - expect(diff.path).to.equal('div'); + expect(htmlA).to.not.equal(htmlB); }); }); describe('text', () => { it('changed text', () => { - const diff = getSemanticDomDiff('
foo
', '
bar
'); + const htmlA = getDiffableSemanticHTML('
foo
'); + const htmlB = getDiffableSemanticHTML('
bar
'); - expect(diff.message).to.equal('text "foo" was changed to text "bar"'); - expect(diff.path).to.equal('div'); + expect(htmlA).to.not.equal(htmlB); }); it('removed text', () => { - const diff = getSemanticDomDiff('
foo
', '
'); + const htmlA = getDiffableSemanticHTML('
foo
'); + const htmlB = getDiffableSemanticHTML('
'); - expect(diff.message).to.equal('text "foo" has been removed'); - expect(diff.path).to.equal('div'); + expect(htmlA).to.not.equal(htmlB); }); it('added text', () => { - const diff = getSemanticDomDiff('
', '
foo
'); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
foo
'); - 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( - '
foo
', - '
bar
', - ); - - expect(diff.message).to.equal('tag
was changed to tag '); - expect(diff.path).to.equal('div'); + expect(htmlA).to.not.equal(htmlB); }); }); describe('deep changes', () => { it('element changes', () => { - const a = ` + const htmlA = getDiffableSemanticHTML(`
@@ -99,8 +87,9 @@ describe('getSemanticDomDiff()', () => {
- `; - const b = ` + `); + + const htmlB = getDiffableSemanticHTML(`
@@ -112,15 +101,13 @@ describe('getSemanticDomDiff()', () => {
- `; - const diff = getSemanticDomDiff(a, b); + `); - expect(diff.message).to.equal('tag
was changed to tag '); - expect(diff.path).to.equal('div > div#foo > div > div.foo > div.bar.baz.foo'); + expect(htmlA).to.not.equal(htmlB); }); it('attribute changes', () => { - const a = ` + const htmlA = getDiffableSemanticHTML(`
@@ -132,8 +119,9 @@ describe('getSemanticDomDiff()', () => {
- `; - const b = ` + `); + + const htmlB = getDiffableSemanticHTML(`
@@ -145,11 +133,9 @@ describe('getSemanticDomDiff()', () => {
- `; - 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'); + expect(htmlA).to.not.equal(htmlB); }); }); }); @@ -157,184 +143,143 @@ describe('getSemanticDomDiff()', () => { describe('equality', () => { describe('simple', () => { it('element', () => { - const diff = getSemanticDomDiff('
', '
'); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
'); + expect(htmlA).to.equal(htmlB); }); it('attribute', () => { - const diff = getSemanticDomDiff('
', '
'); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
'); + expect(htmlA).to.equal(htmlB); }); it('text', () => { - const diff = getSemanticDomDiff('
foo
', '
foo
'); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
foo
'); + const htmlB = getDiffableSemanticHTML('
foo
'); + expect(htmlA).to.equal(htmlB); }); }); describe('complex', () => { it('large template', () => { - const diff = getSemanticDomDiff(largeTemplate, largeTemplate); - - expect(diff).to.equal(null); - }); - - it('self closing tags', () => { - const diff = getSemanticDomDiff('


', '


'); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML(largeTemplate); + const htmlB = getDiffableSemanticHTML(largeTemplate); + expect(htmlA).to.equal(htmlB); }); }); describe('ordering', () => { it('attributes order', () => { - const diff = getSemanticDomDiff( - '
', - '
', - ); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
'); + expect(htmlA).to.equal(htmlB); }); it('class order', () => { - const diff = getSemanticDomDiff( - '
', - '
', - ); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
'); + expect(htmlA).to.equal(htmlB); }); }); describe('whitespace', () => { it('trailing whitespace in attributes', () => { - const diff = getSemanticDomDiff('
', '
'); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
'); + expect(htmlA).to.equal(htmlB); }); it('trailing whitespace in class', () => { - const diff = getSemanticDomDiff( - '
', - '
', - ); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
'); + expect(htmlA).to.equal(htmlB); }); it('whitespace between classes', () => { - const diff = getSemanticDomDiff( - '
', - '
', - ); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
'); + expect(htmlA).to.equal(htmlB); }); it('whitespace before and after template', () => { - const diff = getSemanticDomDiff('
', '
'); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
'); + const htmlB = getDiffableSemanticHTML('
'); + expect(htmlA).to.equal(htmlB); }); it('whitespace in between nodes', () => { - const diff = getSemanticDomDiff( - '
foo
', - '
foo
', - ); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
foo
'); + const htmlB = getDiffableSemanticHTML('
foo
'); + expect(htmlA).to.equal(htmlB); }); it('whitespace around text nodes', () => { - const diff = getSemanticDomDiff('
foo
', '
foo
'); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
foo
'); + const htmlB = getDiffableSemanticHTML('
foo
'); + expect(htmlA).to.equal(htmlB); }); }); describe('tabs', () => { it('tabs before and after template', () => { - const diff = getSemanticDomDiff('\t\t
\t', '
'); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('\t\t
\t'); + const htmlB = getDiffableSemanticHTML('
'); + expect(htmlA).to.equal(htmlB); }); it('tabs in between nodes', () => { - const diff = getSemanticDomDiff( - '
\t
\t \t \t
', - '
', - ); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
\t
\t \t \t
'); + const htmlB = getDiffableSemanticHTML('
'); + expect(htmlA).to.equal(htmlB); }); it('tabs around text nodes', () => { - const diff = getSemanticDomDiff('
foo
', '
\tfoo\t
'); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
foo
'); + const htmlB = getDiffableSemanticHTML('
\tfoo\t
'); + expect(htmlA).to.equal(htmlB); }); }); describe('newlines', () => { it('newlines before and after template', () => { - const diff = getSemanticDomDiff('\n\n
\n', '
'); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('\n\n
\n'); + const htmlB = getDiffableSemanticHTML('
'); + expect(htmlA).to.equal(htmlB); }); it('newlines in between nodes', () => { - const diff = getSemanticDomDiff( - '
\n
\n \n \n
', - '
', - ); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
\n
\n \n \n
'); + const htmlB = getDiffableSemanticHTML('
'); + expect(htmlA).to.equal(htmlB); }); it('newlines around text nodes', () => { - const diff = getSemanticDomDiff('
foo
', '
\n\n\nfoo\n
'); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
foo
'); + const htmlB = getDiffableSemanticHTML('
\n\n\nfoo\n
'); + expect(htmlA).to.equal(htmlB); }); }); describe('filtered nodes', () => { it('comments', () => { - const diff = getSemanticDomDiff('
foo
', '
foo
'); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
foo
'); + const htmlB = getDiffableSemanticHTML('
foo
'); + expect(htmlA).to.equal(htmlB); }); it('styles', () => { - const diff = getSemanticDomDiff( + const htmlA = getDiffableSemanticHTML( '
foo
', - '
foo
', ); - - expect(diff).to.equal(null); + const htmlB = getDiffableSemanticHTML('
foo
'); + expect(htmlA).to.equal(htmlB); }); it('script', () => { - const diff = getSemanticDomDiff( - '
foo
', - '
foo
', - ); - - expect(diff).to.equal(null); - }); - - it('ignored tags', () => { - const diff = getSemanticDomDiff( - '
foo
', - '
bar
', - { ignoredTags: ['span'] }, - ); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML('
foo
'); + const htmlB = getDiffableSemanticHTML('
foo
'); + expect(htmlA).to.equal(htmlB); }); }); @@ -354,35 +299,10 @@ bar
`; - const diff = getSemanticDomDiff(a, b); - - expect(diff).to.equal(null); + const htmlA = getDiffableSemanticHTML(a); + const htmlB = getDiffableSemanticHTML(b); + expect(htmlA).to.equal(htmlB); }); }); }); - - describe('values', () => { - it('handles strings', () => { - const diff = getSemanticDomDiff('
', ''); - - expect(diff.message).to.equal('tag
was changed to tag '); - }); - }); - - describe('diff tree', () => { - it('returns the left and right side normalized trees', () => { - const diff = getSemanticDomDiff( - ` -
-
- `, - '', - ); - - expect(diff.normalizedLeftHTML).to.equal( - '
', - ); - expect(diff.normalizedRightHTML).to.equal(''); - }); - }); });