Skip to content

Commit

Permalink
feat(queryhandler): add built-in pierce handler (#6509)
Browse files Browse the repository at this point in the history
Adds a handler 'pierce' that pierces shadow roots while querying.
  • Loading branch information
johanbay committed Oct 13, 2020
1 parent f04bec5 commit 8fabe32
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 1 deletion.
55 changes: 54 additions & 1 deletion src/common/QueryHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,60 @@ const _defaultHandler = makeQueryHandler({
element.querySelectorAll(selector),
});

const _builtInHandlers = new Map([['aria', ariaHandler]]);
const pierceHandler = makeQueryHandler({
queryOne: (element, selector) => {
let found: Element | null = null;
const search = (root: Element | ShadowRoot) => {
const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
do {
const currentNode = iter.currentNode as HTMLElement;
if (currentNode.shadowRoot) {
search(currentNode.shadowRoot);
}
if (currentNode instanceof ShadowRoot) {
continue;
}
if (!found && currentNode.matches(selector)) {
found = currentNode;
}
} while (!found && iter.nextNode());
};
if (element instanceof Document) {
element = element.documentElement;
}
search(element);
return found;
},

queryAll: (element, selector) => {
const result: Element[] = [];
const collect = (root: Element | ShadowRoot) => {
const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
do {
const currentNode = iter.currentNode as HTMLElement;
if (currentNode.shadowRoot) {
collect(currentNode.shadowRoot);
}
if (currentNode instanceof ShadowRoot) {
continue;
}
if (currentNode.matches(selector)) {
result.push(currentNode);
}
} while (iter.nextNode());
};
if (element instanceof Document) {
element = element.documentElement;
}
collect(element);
return result;
},
});

const _builtInHandlers = new Map([
['aria', ariaHandler],
['pierce', pierceHandler],
]);
const _queryHandlers = new Map(_builtInHandlers);

/**
Expand Down
39 changes: 39 additions & 0 deletions test/queryselector.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,45 @@ describe('querySelector', function () {
});
});

describe('pierceHandler', function () {
beforeEach(async () => {
const { page } = getTestState();
await page.setContent(
`<script>
const div = document.createElement('div');
const shadowRoot = div.attachShadow({mode: 'open'});
const div1 = document.createElement('div');
div1.textContent = 'Hello';
div1.className = 'foo';
const div2 = document.createElement('div');
div2.textContent = 'World';
div2.className = 'foo';
shadowRoot.appendChild(div1);
shadowRoot.appendChild(div2);
document.documentElement.appendChild(div);
</script>`
);
});
it('should find first element in shadow', async () => {
const { page } = getTestState();
const div = await page.$('pierce/.foo');
const text = await div.evaluate(
(element: Element) => element.textContent
);
expect(text).toBe('Hello');
});
it('should find all elements in shadow', async () => {
const { page } = getTestState();
const divs = await page.$$('pierce/.foo');
const text = await Promise.all(
divs.map((div) =>
div.evaluate((element: Element) => element.textContent)
)
);
expect(text.join(' ')).toBe('Hello World');
});
});

// The tests for $$eval are repeated later in this file in the test group 'QueryAll'.
// This is done to also test a query handler where QueryAll returns an Element[]
// as opposed to NodeListOf<Element>.
Expand Down

0 comments on commit 8fabe32

Please sign in to comment.