',
+ (expect, subject, value) => {
+ const nodes = subject.childNodes || makeAttachedDOMNodeList(subject);
+ const isHtml = isInsideHtmlDocument(
+ subject.childNodes ? subject : nodes
+ );
+ const valueType = expect.findTypeOf(value);
+ let spec = value;
+
+ if (valueType.is('DOMElement')) {
+ spec = convertDOMNodeToSatisfySpec(value, isHtml);
+ } else if (valueType.is('string')) {
+ const documentFragment = isHtml
+ ? parseHtml(value, true)
+ : parseXml(value);
+
+ if (documentFragment.childNodes.length !== 1) {
+ throw new Error(
+ 'HTMLElement to contain string: Only a single node is supported'
+ );
+ }
+
+ spec = convertDOMNodeToSatisfySpec(
+ documentFragment.childNodes[0],
+ isHtml
+ );
+
+ if (typeof spec === 'string') {
+ throw new Error(
+ 'HTMLElement to contain string: please provide a HTML structure as a string'
+ );
+ }
+
+ expect.argsOutput = output =>
+ output.appendInspected(documentFragment.childNodes[0]);
+
+ ensureSupportedSpecOptions(spec);
+ }
+
+ const scoredElements = findMatchesWithGoodScore(nodes, spec);
+
+ if (expect.flags.not) {
+ if (scoredElements.length > 0) {
+ return expect.withError(
+ () =>
+ expect(
+ scoredElements.map(({ element }) => element),
+ 'not to have an item satisfying',
+ spec
+ ),
+ () => {
+ const bestMatch = scoredElements[0].element;
+
+ expect.subjectOutput = output =>
+ expect.inspect(subject, Infinity, output);
+
+ expect.fail({
+ diff: (output, diff, inspect, equal) => {
+ return output
+ .error('Found:')
+ .nl(2)
+ .appendInspected(bestMatch);
+ }
+ });
+ }
+ );
+ }
+ } else {
+ if (scoredElements.length === 0) {
+ expect.subjectOutput = output =>
+ expect.inspect(subject, Infinity, output);
+ expect.fail();
+ }
+
+ return expect.withError(
+ () =>
+ expect(
+ scoredElements.map(({ element }) => element),
+ 'to have an item satisfying',
+ spec
+ ),
+ () => {
+ const bestMatch = scoredElements[0].element;
+
+ return expect(bestMatch, 'to satisfy', spec);
+ }
+ );
+ }
+ }
+ );
}
};
diff --git a/test/index.spec.js b/test/index.spec.js
index ce3b6ff3..0116bb3f 100644
--- a/test/index.spec.js
+++ b/test/index.spec.js
@@ -2858,4 +2858,639 @@ describe('unexpected-dom', () => {
'>'
);
});
+
+ describe('to contain', () => {
+ describe('on a DOMDocument', () => {
+ describe('when given a DOMElement', () => {
+ it('succeeds if the given structure is present', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML to contain',
+ parseHtmlNode('Jane Doe ')
+ );
+ });
+ });
+ });
+
+ describe('on a DOMDocumentFragment', () => {
+ it('succeeds if the given structure is present', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML fragment to contain',
+ 'Jane Doe '
+ );
+ });
+ });
+
+ describe('on a DOMElement', () => {
+ it('succeeds if the given structure is present', () => {
+ expect(
+ parseHtmlNode(
+ 'Hello Jane Doe
'
+ ),
+ 'to contain',
+ 'Jane Doe '
+ );
+ });
+ });
+
+ describe('on a DOMNodeList', () => {
+ it('succeeds if the given structure is present', () => {
+ expect(
+ 'Nothing here
Hello Jane Doe
',
+ 'when parsed as HTML fragment',
+ 'queried for',
+ 'div',
+ 'to contain',
+ 'Jane Doe '
+ );
+ });
+ });
+
+ describe('on an XMLDocument', () => {
+ it('succeeds if the given structure is present', () => {
+ expect(
+ 'foo bax baax ',
+ 'parsed as XML',
+ 'to contain',
+ 'foo '
+ );
+ });
+ });
+
+ describe('when given a DOMElement', () => {
+ it('succeeds if the given structure is present', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ parseHtmlNode('Jane Doe ')
+ );
+ });
+ });
+
+ describe('when given a spec', () => {
+ it('succeeds if the given structure is present', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ {
+ name: 'span',
+ attributes: { class: 'name' },
+ textContent: expect.it('to match', /^Jane/).and('to have length', 8)
+ }
+ );
+ });
+
+ it('supports searching for class names', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ {
+ attributes: { class: 'something-else name' }
+ }
+ );
+ });
+
+ it('supports searching for inline-styles by an object', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ {
+ attributes: { style: { 'background-color': 'red' } }
+ }
+ );
+ });
+
+ it('supports searching for inline-styles by a string', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ {
+ attributes: { style: 'background-color: red' }
+ }
+ );
+ });
+
+ it('supports using regexps on the tag name', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ {
+ name: /^(i|span)$/,
+ textContent: 'Hello'
+ }
+ );
+ });
+
+ it('supports using expect.it on the tag name', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ {
+ name: expect.it('to have length', 1),
+ textContent: 'Hello'
+ }
+ );
+ });
+
+ it('supports using regexps on the class name', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ {
+ attributes: {
+ class: /^name something/
+ }
+ }
+ );
+ });
+
+ it('supports using expect.it on the class name', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ {
+ attributes: {
+ class: expect.it('to end with', 'else')
+ },
+ textContent: 'Jane Doe'
+ }
+ );
+ });
+
+ it('supports using declaring that the class should be undefined', () => {
+ expect(
+ 'Hello! Hello
',
+ 'when parsed as HTML',
+ 'to contain',
+ {
+ attributes: {
+ class: undefined
+ },
+ textContent: 'Hello'
+ }
+ );
+ });
+
+ it('supports searching for boolean attributes', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ {
+ name: 'input',
+ attributes: { checked: true }
+ }
+ );
+ });
+
+ it('supports searching for false boolean attributes', () => {
+ expect(
+ '
',
+ 'when parsed as HTML',
+ 'to contain',
+ {
+ name: 'input',
+ attributes: { checked: undefined }
+ }
+ );
+ });
+
+ it('supports searching for a child element', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ {
+ name: 'span',
+ children: [
+ parseHtmlNode('Hello '),
+ parseHtmlNode('')
+ ]
+ }
+ );
+ });
+
+ it('supports the onlyAttributes flag', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ {
+ name: 'span',
+ attributes: {
+ class: 'greeting'
+ },
+ onlyAttributes: true
+ }
+ );
+ });
+ });
+
+ describe('when given a string', () => {
+ it('succeeds if the given structure is present', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ 'Jane Doe '
+ );
+ });
+
+ it('fails when given more than on node', () => {
+ expect(
+ () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ 'Jane Doe !'
+ );
+ },
+ 'to throw',
+ 'HTMLElement to contain string: Only a single node is supported'
+ );
+ });
+ });
+
+ it('supports only stating a subset of the classes', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ 'Jane Doe '
+ );
+ });
+
+ it('supports searching for boolean attributes', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ ' '
+ );
+ });
+
+ it('supports searching for style values', () => {
+ expect(
+ 'Hello Jane Doe !
',
+ 'when parsed as HTML',
+ 'to contain',
+ 'Jane Doe '
+ );
+ });
+
+ it('takes ignore comments into account when searching children', () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ 'Hello '
+ );
+ });
+
+ it('fails searching for a plain string', () => {
+ expect(
+ () => {
+ expect(
+ 'Hello Jane Doe
',
+ 'when parsed as HTML',
+ 'to contain',
+ 'Jane Doe'
+ );
+ },
+ 'to throw',
+ 'HTMLElement to contain string: please provide a HTML structure as a string'
+ );
+ });
+
+ it('fails when matching against an element with no children', () => {
+ expect(
+ () => {
+ expect(
+ parseHtmlNode('
'),
+ 'to contain',
+ 'Jane Doe '
+ );
+ },
+ 'to throw',
+ 'expected
to contain Jane Doe '
+ );
+ });
+
+ it('should not match directly on the subject', () => {
+ expect(
+ () => {
+ expect(
+ parseHtmlNode(
+ 'Hello Jane Doe '
+ ),
+ 'to contain',
+ 'Hello '
+ );
+ },
+ 'to throw',
+ 'expected\n' +
+ '\n' +
+ ' Hello \n' +
+ ' Jane Doe \n' +
+ ' \n' +
+ 'to contain Hello \n' +
+ '\n' +
+ '\n' +
+ " // missing { name: 'span', attributes: {}, children: [ 'Hello' ] }\n" +
+ ' Hello\n' +
+ ' '
+ );
+ });
+
+ it('fails without a diff if no good candidates can be found in the given structure', () => {
+ expect(
+ () => {
+ expect(
+ parseHtmlNode(
+ ''
+ ),
+ 'to contain',
+ 'John Doe '
+ );
+ },
+ 'to throw',
+ 'expected \n' +
+ 'to contain John Doe '
+ );
+ });
+
+ it('fails with a diff if the given structure is not present', () => {
+ expect(
+ () => {
+ expect(
+ parseHtmlNode(
+ 'Hello Jane Doe
'
+ ),
+ 'to contain',
+ 'John Doe '
+ );
+ },
+ 'to throw',
+ 'expected\n' +
+ '\n' +
+ ' Hello \n' +
+ ' \n' +
+ ' Jane Doe \n' +
+ '
\n' +
+ 'to contain John Doe \n' +
+ '\n' +
+ '\n' +
+ " Jane Doe // should equal 'John Doe'\n" +
+ ' //\n' +
+ ' // -Jane Doe\n' +
+ ' // +John Doe\n' +
+ ' '
+ );
+ });
+
+ it('allows tag names to be different while finding the best match', () => {
+ expect(
+ () => {
+ expect(
+ parseHtmlNode(
+ 'Hello Jane Doe and
John Doe
'
+ ),
+ 'to contain',
+ 'Jane Doe
'
+ );
+ },
+ 'to throw',
+ 'expected\n' +
+ '\n' +
+ '
Hello \n' +
+ ' \n' +
+ '
Jane Doe \n' +
+ ' and\n' +
+ '
John Doe
\n' +
+ '
\n' +
+ 'to contain Jane Doe
\n' +
+ '\n' +
+ "Jane Doe '
+ );
+ });
+
+ it('matches on sub-trees when searching for the best match', () => {
+ expect(
+ () => {
+ expect(
+ parseHtmlNode(
+ 'Hello Jane Doe and
John Doe
'
+ ),
+ 'to contain',
+ 'Jane Doe '
+ );
+ },
+ 'to throw',
+ 'expected\n' +
+ '\n' +
+ '
Hello \n' +
+ ' \n' +
+ '
... \n' +
+ ' and\n' +
+ '
John Doe
\n' +
+ '
\n' +
+ 'to contain Jane Doe \n' +
+ '\n' +
+ '\n' +
+ " Jane Doe \n' +
+ ' '
+ );
+ });
+
+ it('matches more strongly on ids when showing the best match', () => {
+ expect(
+ () => {
+ expect(
+ parseHtmlNode(
+ 'Hello Jane Doe and John Doe
'
+ ),
+ 'to contain',
+ 'John Doe '
+ );
+ },
+ 'to throw',
+ 'expected\n' +
+ '\n' +
+ ' Hello \n' +
+ ' \n' +
+ ' \n' +
+ ' Jane Doe\n' +
+ ' \n' +
+ ' and\n' +
+ ' John Doe \n' +
+ '
\n' +
+ 'to contain John Doe \n' +
+ '\n' +
+ '\n' +
+ " Jane Doe // should equal 'John Doe'\n" +
+ ' //\n' +
+ ' // -Jane Doe\n' +
+ ' // +John Doe\n' +
+ ' '
+ );
+ });
+
+ it('fails if the children is expected but the target is empty', () => {
+ expect(
+ () => {
+ expect(
+ parseHtmlNode('
'),
+ 'to contain',
+ 'Hello '
+ );
+ },
+ 'to throw',
+ 'expected
to contain Hello \n' +
+ '\n' +
+ '\n' +
+ " // missing { name: 'i', attributes: {}, children: [ 'Hello' ] }\n" +
+ ' '
+ );
+ });
+
+ it('fails if the an ignored child is expected but the target is empty', () => {
+ expect(
+ () => {
+ expect(
+ parseHtmlNode('
'),
+ 'to contain',
+ ' '
+ );
+ },
+ 'to throw',
+ 'expected
to contain \n' +
+ '\n' +
+ '\n' +
+ ' // missing \n' +
+ ' '
+ );
+ });
+
+ it('fails if more children is expected than what is available in the target', () => {
+ expect(
+ () => {
+ expect(
+ parseHtmlNode(
+ 'Hello world
'
+ ),
+ 'to contain',
+ 'Hello ! '
+ );
+ },
+ 'to throw',
+ 'expected ... ...
\n' +
+ 'to contain Hello ! \n' +
+ '\n' +
+ '\n' +
+ ' Hello \n' +
+ ' world \n' +
+ " // missing '!'\n" +
+ ' '
+ );
+ });
+
+ it('fails if less children is expected than what is available in the target', () => {
+ expect(
+ () => {
+ expect(
+ parseHtmlNode(
+ 'Hello world !
'
+ ),
+ 'to contain',
+ 'Hello world '
+ );
+ },
+ 'to throw',
+ 'expected ... ... !
\n' +
+ 'to contain Hello world \n' +
+ '\n' +
+ '\n' +
+ ' Hello \n' +
+ ' world \n' +
+ ' ! // should be removed\n' +
+ ' '
+ );
+ });
+ });
+
+ describe('not to contain', () => {
+ it('succeeds if the given structure is not present', () => {
+ expect(
+ parseHtmlNode(
+ 'Hello Jane Doe and John Doe
'
+ ),
+ 'not to contain',
+ 'John Doe '
+ );
+ });
+
+ it("succeeds if the given structure doesn't match any descendant elements at all", () => {
+ expect(
+ parseHtmlNode(
+ ''
+ ),
+ 'not to contain',
+ 'John Doe '
+ );
+ });
+
+ it('succeeds if the element has no children', () => {
+ expect(
+ parseHtmlNode('
'),
+ 'not to contain',
+ 'Jane Doe '
+ );
+ });
+
+ it('shows a diff if the given structure is present', () => {
+ expect(
+ () => {
+ expect(
+ parseHtmlNode(
+ 'Hello Jane Doe and John Doe
'
+ ),
+ 'not to contain',
+ 'Jane Doe '
+ );
+ },
+ 'to throw',
+ 'expected\n' +
+ '\n' +
+ ' Hello \n' +
+ ' \n' +
+ ' \n' +
+ ' Jane Doe\n' +
+ ' \n' +
+ ' and\n' +
+ ' John Doe \n' +
+ '
\n' +
+ 'not to contain Jane Doe \n' +
+ '\n' +
+ 'Found:\n' +
+ '\n' +
+ '\n' +
+ ' Jane Doe\n' +
+ ' '
+ );
+ });
+ });
});