Skip to content

Commit

Permalink
Merge pull request #2090 from mainmatter/root-element-shadow-dom
Browse files Browse the repository at this point in the history
Support ShadowRoot, add tests for rootElement, fixes #2089
  • Loading branch information
BobrImperator authored Apr 9, 2024
2 parents 30feace + e9ef45a commit b755beb
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 5 deletions.
8 changes: 7 additions & 1 deletion API.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Once installed the DOM element assertions are available at `assert.dom(...).*`:
**Parameters**

* `target` **([string][114] | [HTMLElement][115])** A CSS selector that can be used to find elements using [`querySelector()`][116], or an \[HTMLElement]\[] (Not all assertions support both target types.) (optional, default `rootElement` or `document`)
* `rootElement` **[HTMLElement][115]?** The root element of the DOM in which to search for the `target` (optional, default `document`)
* `rootElement` **([HTMLElement][115] | [Document][152] | [ShadowRoot][153] | [null][154])?** The root element of the DOM in which to search for the `target` (optional, defaults `document` when `null` or not provided)

**Examples**

Expand Down Expand Up @@ -1194,3 +1194,9 @@ assert.dom('section#block').doesNotHaveTagName('div');
[150]: #hasValue

[151]: https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName

[152]: https://developer.mozilla.org/en-US/docs/Web/API/Document

[153]: https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot

[154]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/null
152 changes: 152 additions & 0 deletions packages/qunit-dom/lib/__tests__/root-element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { describe, beforeEach, test, expect } from 'vitest';

import TestAssertions from '../helpers/test-assertions';

describe('assert.dom(..., rootElement)', () => {
let assert: TestAssertions;

beforeEach(() => {
assert = new TestAssertions();
});

test('passing an Element as rootElement', () => {
document.body.innerHTML = `
<span class="target">dedcoy<span>
<h1 class="parent">
<span class="target">real target<span>
</h1>
`;

const rootElement = document.querySelector('.parent');

assert.dom('.target', rootElement).exists({ count: 1 });
assert.dom('.target', rootElement).hasText('real target');

expect(assert.results).toEqual([
{
result: true,
actual: 'Element .target exists once',
expected: 'Element .target exists once',
message: 'Element .target exists once',
},
{
result: true,
actual: 'real target',
expected: 'real target',
message: 'Element .target has text "real target"',
},
]);
});

test('not passing anything as rootElement', () => {
document.body.innerHTML = `
<span class="target">decoy<span>
<h1 class="parent">
<span class="target">real target<span>
</h1>
`;

assert.dom('.target').exists({ count: 2 });

expect(assert.results).toEqual([
{
result: true,
actual: 'Element .target exists twice',
expected: 'Element .target exists twice',
message: 'Element .target exists twice',
},
]);
});

test('passing document as rootElement', () => {
document.body.innerHTML = `
<span class="target">decoy<span>
<h1 class="parent">
<span class="target">real target<span>
</h1>
`;

assert.dom('.target', document).exists({ count: 2 });

expect(assert.results).toEqual([
{
result: true,
actual: 'Element .target exists twice',
expected: 'Element .target exists twice',
message: 'Element .target exists twice',
},
]);
});

test('passing null as rootElement', () => {
document.body.innerHTML = `
<span class="target">decoy<span>
<h1 class="parent">
<span class="target">real target<span>
</h1>
`;

assert.dom('.target', null).exists({ count: 2 });

expect(assert.results).toEqual([
{
result: true,
actual: 'Element .target exists twice',
expected: 'Element .target exists twice',
message: 'Element .target exists twice',
},
]);
});

test('passing shadow root as rootElement', () => {
document.body.innerHTML = `
<div id="container">
<span class="target">decoy<span>
</div>
`;

const container = document.getElementById('container');
const shadowRoot = container.attachShadow({ mode: 'closed' });

shadowRoot.innerHTML = '<span class="target">real target<span>';

assert.dom('.target').exists({ count: 1 }, 'Only decoy element is found outside shadow root');
assert.dom('.target').hasText('real target', 'decoy element text');

assert.dom('.target', shadowRoot).exists({ count: 1 }, 'Only target found in shadow root');
assert.dom('.target', shadowRoot).hasText('real target', 'Target element text');

console.log(assert.results);

expect(assert.results).toEqual([
{
result: true,
actual: 'Element .target exists once',
expected: 'Element .target exists once',
message: 'Only decoy element is found outside shadow root',
},
{
result: false,
actual: 'decoy',
expected: 'real target',
message: 'decoy element text',
},
{
result: true,
actual: 'Element .target exists once',
expected: 'Element .target exists once',
message: 'Only target found in shadow root',
},
{
result: true,
actual: 'real target',
expected: 'real target',
message: 'Target element text',
},
]);
});
});
2 changes: 1 addition & 1 deletion packages/qunit-dom/lib/assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type ActualCSSStyleDeclaration = Partial<Record<CSSStyleDeclarationProperty, unk
export default class DOMAssertions {
constructor(
private target: string | Element | null,
private rootElement: Element | Document,
private rootElement: RootElement,
private testContext: Assert
) {}

Expand Down
2 changes: 1 addition & 1 deletion packages/qunit-dom/lib/helpers/test-assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import DOMAssertions, { type AssertionResult } from '../assertions.js';
export default class TestAssertions {
public results: AssertionResult[] = [];

dom(target: string | Element | null, rootElement?: Element) {
dom(target: string | Element | null, rootElement?: RootElement) {
return new DOMAssertions(target, rootElement || document, this as any);
}

Expand Down
9 changes: 7 additions & 2 deletions packages/qunit-dom/lib/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ import DOMAssertions from './assertions.js';
import { getRootElement } from './root-element.js';

declare global {
type RootElement = Element | Document | ShadowRoot | null;

interface Assert {
dom(target?: string | Element | null, rootElement?: Element): DOMAssertions;
dom(target?: string | Element | null, rootElement?: RootElement): DOMAssertions;
}
}

export default function (assert: Assert) {
assert.dom = function (target?: string | Element | null, rootElement?: Element): DOMAssertions {
assert.dom = function (
target?: string | Element | null,
rootElement?: RootElement
): DOMAssertions {
if (!isValidRootElement(rootElement)) {
throw new Error(`${rootElement} is not a valid root element`);
}
Expand Down

0 comments on commit b755beb

Please sign in to comment.