diff --git a/CHANGELOG.md b/CHANGELOG.md index d054e84fa592..3851a8378414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Fixes +- `[expect]` Compare DOM nodes even if there are multiple Node classes ([#8064](https://github.com/facebook/jest/pull/8064)) + ### Chore & Maintenance - `[pretty-format]`: Use `react-is` instead of manual `$$typeof` checks ([#8060](https://github.com/facebook/jest/pull/8060)) diff --git a/packages/expect/src/__tests__/toEqual-dom.test.js b/packages/expect/src/__tests__/toEqual-dom.test.js new file mode 100644 index 000000000000..980189261886 --- /dev/null +++ b/packages/expect/src/__tests__/toEqual-dom.test.js @@ -0,0 +1,218 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @jest-environment jsdom + */ +/* eslint-env browser*/ + +describe('toEqual', () => { + describe('duck type', () => { + // https://github.com/facebook/jest/issues/7786 + + const createElement = (name, ...childNodes) => ({ + childNodes, + nodeType: 1, + tagName: name.toUpperCase(), + }); + + const createTextNode = data => ({ + data, + nodeType: 3, + }); + + const createDocumentFragment = (...children) => ({ + children, + nodeType: 11, + }); + + describe('Text', () => { + test('isNot false', () => { + const data = 'deep equal'; + + const a = createTextNode(data); + const b = createTextNode(data); + + expect(a).toEqual(b); + expect(b).toEqual(a); + }); + + test('isNot true', () => { + const a = createTextNode('not deep equal a'); + const b = createTextNode('not deep equal b'); + + expect(a).not.toEqual(b); + expect(b).not.toEqual(a); + }); + }); + + describe('Element', () => { + test('isNot false', () => { + const name = 'span'; + const data = 'deep equal'; + + const a = createElement(name, createTextNode(data)); + const b = createElement(name, createTextNode(data)); + + expect(a).toEqual(b); + expect(b).toEqual(a); + }); + + test('isNot true', () => { + const data = 'not deep equal'; + + const a = createElement('strong', createTextNode(data)); + const b = createElement('span', createTextNode(data)); + + expect(a).not.toEqual(b); + expect(b).not.toEqual(a); + }); + }); + + describe('Fragment', () => { + test('isNot false', () => { + const name1 = 'strong'; + const name2 = 'span'; + const data1 = 'deep'; + const data2 = 'equal'; + + const a = createDocumentFragment( + createElement(name1, createTextNode(data1)), + createElement(name2, createTextNode(data2)), + ); + const b = createDocumentFragment( + createElement(name1, createTextNode(data1)), + createElement(name2, createTextNode(data2)), + ); + + expect(a).toEqual(b); + expect(b).toEqual(a); + }); + + test('isNot true', () => { + const name = 'span'; + const data1 = 'not'; + const data2 = 'deep equal'; + + const a = createDocumentFragment( + createElement('strong', createTextNode(data1)), + createElement(name, createTextNode(data2)), + ); + const b = createDocumentFragment( + createElement(name, createTextNode(data1)), + createElement(name, createTextNode(data2)), + ); + + expect(a).not.toEqual(b); + expect(b).not.toEqual(a); + }); + }); + }); + + describe('document', () => { + describe('createTextNode', () => { + test('isNot false', () => { + const data = 'deep equal'; + + const a = document.createTextNode(data); + const b = document.createTextNode(data); + + expect(a).toEqual(b); + expect(b).toEqual(a); + }); + + test('isNot true', () => { + const a = document.createTextNode('not deep equal a'); + const b = document.createTextNode('not deep equal b'); + + expect(a).not.toEqual(b); + expect(b).not.toEqual(a); + }); + }); + + describe('createElement', () => { + test('isNot false', () => { + const name = 'span'; + const data = 'deep equal'; + + const a = document.createElement(name); + const b = document.createElement(name); + a.appendChild(document.createTextNode(data)); + b.appendChild(document.createTextNode(data)); + + expect(a).toEqual(b); + expect(b).toEqual(a); + }); + + test('isNot true', () => { + const data = 'not deep equal'; + + const a = document.createElement('strong'); + const b = document.createElement('span'); + a.appendChild(document.createTextNode(data)); + b.appendChild(document.createTextNode(data)); + + expect(a).not.toEqual(b); + expect(b).not.toEqual(a); + }); + }); + + describe('createDocumentFragment', () => { + test('isNot false', () => { + const name1 = 'strong'; + const name2 = 'span'; + const data1 = 'deep'; + const data2 = 'equal'; + + const aSpan1 = document.createElement(name1); + const bSpan1 = document.createElement(name1); + aSpan1.appendChild(document.createTextNode(data1)); + bSpan1.appendChild(document.createTextNode(data1)); + + const aSpan2 = document.createElement(name2); + const bSpan2 = document.createElement(name2); + aSpan2.appendChild(document.createTextNode(data2)); + bSpan2.appendChild(document.createTextNode(data2)); + + const a = document.createDocumentFragment(); + const b = document.createDocumentFragment(); + a.appendChild(aSpan1); + a.appendChild(aSpan2); + b.appendChild(bSpan1); + b.appendChild(bSpan2); + + expect(a).toEqual(b); + expect(b).toEqual(a); + }); + + test('isNot true', () => { + const name = 'span'; + const data1 = 'not'; + const data2 = 'deep equal'; + + const aSpan1 = document.createElement('strong'); + const bSpan1 = document.createElement(name); + aSpan1.appendChild(document.createTextNode(data1)); + bSpan1.appendChild(document.createTextNode(data1)); + + const aSpan2 = document.createElement(name); + const bSpan2 = document.createElement(name); + aSpan2.appendChild(document.createTextNode(data2)); + bSpan2.appendChild(document.createTextNode(data2)); + + const a = document.createDocumentFragment(); + const b = document.createDocumentFragment(); + a.appendChild(aSpan1); + a.appendChild(aSpan2); + b.appendChild(bSpan1); + b.appendChild(bSpan2); + console.log(a.innerHTML); + + expect(a).not.toEqual(b); + expect(b).not.toEqual(a); + }); + }); + }); +}); diff --git a/packages/expect/src/jasmineUtils.ts b/packages/expect/src/jasmineUtils.ts index 7636ec9c2274..a28db744dee1 100644 --- a/packages/expect/src/jasmineUtils.ts +++ b/packages/expect/src/jasmineUtils.ts @@ -247,10 +247,14 @@ export function isA(typeName: string, value: unknown) { return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; } -function isDomNode(obj: any): obj is Node { - // In some test environments (e.g. "node") there is no `Node` even though - // we might be comparing things that look like DOM nodes. - return typeof Node !== 'undefined' && obj instanceof Node; +function isDomNode(obj: any): boolean { + return ( + obj !== null && + typeof obj === 'object' && + typeof obj.nodeType === 'number' && + typeof obj.nodeName === 'string' && + typeof obj.isEqualNode === 'function' + ); } export function fnNameFor(func: Function) {