Skip to content

Commit aee988d

Browse files
committed
package fix up for repository migration
1 parent ef05c1b commit aee988d

File tree

2 files changed

+193
-157
lines changed

2 files changed

+193
-157
lines changed

README.md

Lines changed: 122 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,105 @@
1-
[![Build Status](https://travis-ci.org/Georgegriff/query-selector-shadow-dom.svg?branch=main)](https://travis-ci.org/Georgegriff/query-selector-shadow-dom) [![npm version](https://badge.fury.io/js/query-selector-shadow-dom.svg)](https://badge.fury.io/js/query-selector-shadow-dom) [![codecov](https://codecov.io/gh/Georgegriff/query-selector-shadow-dom/branch/main/graph/badge.svg)](https://codecov.io/gh/Georgegriff/query-selector-shadow-dom)
1+
[![Build Status](https://travis-ci.org/Georgegriff/query-selector-shadow-dom.svg?branch=main)](https://travis-ci.org/Georgegriff/query-selector-shadow-dom) [![npm version](https://badge.fury.io/js/query-selector-shadow-dom.svg)](https://badge.fury.io/js/query-selector-shadow-dom) [![codecov](https://codecov.io/gh/Georgegriff/query-selector-shadow-dom/branch/main/graph/badge.svg)](https://codecov.io/gh/Georgegriff/query-selector-shadow-dom)
2+
23
# query-selector-shadow-dom
3-
querySelector that can pierce Shadow DOM roots without knowing the path through nested shadow roots. Useful for automated testing of Web Components e.g. with Selenium, Puppeteer.
44

5+
querySelector that can pierce Shadow DOM roots without knowing the path through nested shadow roots. Useful for automated testing of Web Components e.g. with Selenium, Puppeteer.
56

6-
[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/G2G221OBA)
77
```javascript
8-
98
// available as an ES6 module for importing in Browser environments
109

11-
import { querySelectorAllDeep, querySelectorDeep } from 'query-selector-shadow-dom';
12-
10+
import {
11+
querySelectorAllDeep,
12+
querySelectorDeep,
13+
} from "query-selector-shadow-dom";
1314
```
1415

1516
## What is a nested shadow root?
17+
1618
![Image of Shadow DOM elements in dev tools](./Chrome-example.png)
1719
You can see that `.dropdown-item:not([hidden])` (Open downloads folder) is several layers deep in shadow roots, most tools will make you do something like
1820

1921
```javascript
20-
document.querySelector("body > downloads-manager").shadowRoot.querySelector("#toolbar").shadowRoot.querySelector(".dropdown-item:not([hidden])")
22+
document
23+
.querySelector("body > downloads-manager")
24+
.shadowRoot.querySelector("#toolbar")
25+
.shadowRoot.querySelector(".dropdown-item:not([hidden])");
2126
```
27+
2228
EW!
2329

2430
with query-selector-shadow-dom:
2531

2632
```javascript
27-
import { querySelectorAllDeep, querySelectorDeep } from 'query-selector-shadow-dom';
33+
import {
34+
querySelectorAllDeep,
35+
querySelectorDeep,
36+
} from "query-selector-shadow-dom";
2837
querySelectorDeep(".dropdown-item:not([hidden])");
2938
```
3039

3140
## API
41+
3242
- querySelectorAllDeep - mirrors `querySelectorAll` from the browser, will return an `Array` of elements matching the query
3343
- querySelectorDeep - mirrors `querySelector` from the browser, will return the `first` matching element of the query.
3444
- collectAllElementsDeep - collects all elements on the page, including shadow dom
3545

3646
Both of the methods above accept a 2nd parameter, see section `Provide alternative node`. This will change the starting element to search from i.e. it will find ancestors of that node that match the query.
3747

3848
## Known limitations
49+
3950
- Source ordering of results may not be preserved. Due to the nature of how this library works, by breaking down selectors into parts, when using multiple selectors (e.g. split by commas) the results will be based on the order of the query, not the order the result appear in the dom. This is different from the native `querySelectorAll` functionality. You can read more about this here: https://github.com/Georgegriff/query-selector-shadow-dom/issues/54
4051

4152
## Plugins
4253

4354
### WebdriverIO
55+
4456
This plugin implements a custom selector strategy: https://webdriver.io/docs/selectors.html#custom-selector-strategies
4557

4658
```javascript
47-
4859
// make sure you have selenium standalone running
49-
const { remote } = require('webdriverio');
50-
const { locatorStrategy } = require('query-selector-shadow-dom/plugins/webdriverio');
60+
const { remote } = require("webdriverio");
61+
const {
62+
locatorStrategy,
63+
} = require("query-selector-shadow-dom/plugins/webdriverio");
5164

5265
(async () => {
53-
const browser = await remote({
54-
logLevel: 'error',
55-
path: '/wd/hub',
56-
capabilities: {
57-
browserName: 'chrome'
58-
}
59-
})
60-
61-
// The magic - registry custom strategy
62-
browser.addLocatorStrategy('shadow', locatorStrategy);
63-
64-
65-
// now you have a `shadow` custom locator.
66-
67-
// All elements on the page
68-
await browser.waitUntil(() => browser.custom$("shadow", ".btn-in-shadow-dom"));
69-
const elements = await browser.$$("*");
70-
71-
const elementsShadow = await browser.custom$$("shadow", "*");
72-
73-
console.log("All Elements on Page Excluding Shadow Dom", elements.length);
74-
console.log("All Elements on Page Including Shadow Dom", elementsShadow.length);
75-
76-
77-
await browser.url('http://127.0.0.1:5500/test/')
78-
// find input element in shadow dom
79-
const input = await browser.custom$('shadow', '#type-to-input');
80-
// type to input ! Does not work in firefox, see above.
81-
await input.setValue('Typed text to input');
82-
// Firefox workaround
83-
// await browser.execute((input, val) => input.value = val, input, 'Typed text to input')
84-
85-
await browser.deleteSession()
86-
})().catch((e) => console.error(e))
66+
const browser = await remote({
67+
logLevel: "error",
68+
path: "/wd/hub",
69+
capabilities: {
70+
browserName: "chrome",
71+
},
72+
});
73+
74+
// The magic - registry custom strategy
75+
browser.addLocatorStrategy("shadow", locatorStrategy);
76+
77+
// now you have a `shadow` custom locator.
78+
79+
// All elements on the page
80+
await browser.waitUntil(() =>
81+
browser.custom$("shadow", ".btn-in-shadow-dom")
82+
);
83+
const elements = await browser.$$("*");
84+
85+
const elementsShadow = await browser.custom$$("shadow", "*");
86+
87+
console.log("All Elements on Page Excluding Shadow Dom", elements.length);
88+
console.log(
89+
"All Elements on Page Including Shadow Dom",
90+
elementsShadow.length
91+
);
92+
93+
await browser.url("http://127.0.0.1:5500/test/");
94+
// find input element in shadow dom
95+
const input = await browser.custom$("shadow", "#type-to-input");
96+
// type to input ! Does not work in firefox, see above.
97+
await input.setValue("Typed text to input");
98+
// Firefox workaround
99+
// await browser.execute((input, val) => input.value = val, input, 'Typed text to input')
100+
101+
await browser.deleteSession();
102+
})().catch((e) => console.error(e));
87103
```
88104

89105
#### How is this different to `shadow$`
@@ -96,43 +112,45 @@ You can see that `.dropdown-item:not([hidden])` (Open downloads folder) is sever
96112
You would have to construct a path via css or javascript all the way through to find the right element.
97113

98114
```javascript
99-
const { remote } = require('webdriverio')
100-
const { locatorStrategy } = require('query-selector-shadow-dom/plugins/webdriverio');
115+
const { remote } = require("webdriverio");
116+
const {
117+
locatorStrategy,
118+
} = require("query-selector-shadow-dom/plugins/webdriverio");
101119

102120
(async () => {
103-
const browser = await remote({capabilities: {browserName: 'chrome'}})
121+
const browser = await remote({ capabilities: { browserName: "chrome" } });
104122

105-
browser.addLocatorStrategy('shadow', locatorStrategy);
123+
browser.addLocatorStrategy("shadow", locatorStrategy);
106124

107-
await browser.url('chrome://downloads')
108-
const moreActions = await browser.custom$('shadow', '#moreActions');
109-
await moreActions.click();
110-
const span = await browser.custom$('shadow', '.dropdown-item:not([hidden])');
111-
const text = await span.getText()
112-
// prints `Open downloads folder`
113-
console.log(text);
125+
await browser.url("chrome://downloads");
126+
const moreActions = await browser.custom$("shadow", "#moreActions");
127+
await moreActions.click();
128+
const span = await browser.custom$("shadow", ".dropdown-item:not([hidden])");
129+
const text = await span.getText();
130+
// prints `Open downloads folder`
131+
console.log(text);
114132

115-
await browser.deleteSession()
116-
})().catch((e) => console.error(e))
133+
await browser.deleteSession();
134+
})().catch((e) => console.error(e));
117135
```
118136

119137
#### Known issues
138+
120139
- https://webdriver.io/blog/2019/02/22/shadow-dom-support.html#browser-support
121140

122141
- From the above, firefox `setValue` does NOT currently work.
123-
`. A workaround for now is to use a custom command (or method on your component object) that sets the input field's value via browser.execute(function).`
142+
`. A workaround for now is to use a custom command (or method on your component object) that sets the input field's value via browser.execute(function).`
124143

125144
- Safari pretty much doesn't work, not really a surprise.
126145

127146
There are some webdriver examples available in the examples folder of this repository.
128147
[WebdriverIO examples](https://github.com/Georgegriff/query-selector-shadow-dom/blob/main/examples/webdriverio)
129148

130-
### Puppeteer
149+
### Puppeteer
131150

132151
Update: As of 5.4.0 Puppeteer now has a built in shadow Dom selector, this module might not be required for Puppeteer anymore.
133152
They don't have any documentation: https://github.com/puppeteer/puppeteer/pull/6509
134153

135-
136154
There are some puppeteer examples available in the examples folder of this repository.
137155

138156
[Puppeteer examples](https://github.com/Georgegriff/query-selector-shadow-dom/blob/main/examples/puppeteer)
@@ -158,84 +176,102 @@ const playwright = require('playwright');
158176

159177
For a full example see: https://github.com/Georgegriff/query-selector-shadow-dom/blob/main/examples/playwright
160178

161-
162179
### Protractor
163180

164181
This project provides a Protractor plugin, which can be enabled in your [`protractor.conf.js`](https://www.protractortest.org/#/api-overview) file:
165182

166183
```javascript
167184
exports.config = {
168-
plugins: [{
169-
package: 'query-selector-shadow-dom/plugins/protractor'
170-
}],
171-
172-
// ... other Protractor-specific config
185+
plugins: [
186+
{
187+
package: "query-selector-shadow-dom/plugins/protractor",
188+
},
189+
],
190+
191+
// ... other Protractor-specific config
173192
};
174193
```
175194

176195
The plugin registers a new [locator](https://www.protractortest.org/#/api?view=ProtractorBy) - `by.shadowDomCss(selector /* string */)`, which can be used in regular Protractor tests:
177196

178197
```javascript
179-
element(by.shadowDomCss('#item-in-shadow-dom'))
198+
element(by.shadowDomCss("#item-in-shadow-dom"));
180199
```
181200

182201
The locator also works with [Serenity/JS](https://serenity-js.org) tests that [use Protractor](https://serenity-js.org/modules/protractor) under the hood:
183202

184203
```typescript
185-
import 'query-selector-shadow-dom/plugins/protractor';
186-
import { Target } from '@serenity-js/protractor'
187-
import { by } from 'protractor';
204+
import "query-selector-shadow-dom/plugins/protractor";
205+
import { Target } from "@serenity-js/protractor";
206+
import { by } from "protractor";
188207

189-
const ElementOfInterest = Target.the('element of interest')
190-
.located(by.shadowDomCss('#item-in-shadow-dom'))
208+
const ElementOfInterest = Target.the("element of interest").located(
209+
by.shadowDomCss("#item-in-shadow-dom")
210+
);
191211
```
192212

193213
See the [end-to-end tests](https://github.com/Georgegriff/query-selector-shadow-dom/blob/features/protractor-locator/test/protractor-locator.e2e.js) for more examples.
194214

195215
## Examples
196216

197217
### Provide alternative node
218+
198219
```javascript
199-
// query from another node
200-
querySelectorShadowDom.querySelectorAllDeep('child', document.querySelector('#startNode'));
201-
// query an iframe
202-
querySelectorShadowDom.querySelectorAllDeep('child', iframe.contentDocument);
220+
// query from another node
221+
querySelectorShadowDom.querySelectorAllDeep(
222+
"child",
223+
document.querySelector("#startNode")
224+
);
225+
// query an iframe
226+
querySelectorShadowDom.querySelectorAllDeep("child", iframe.contentDocument);
203227
```
204228

205229
This library does not allow you to query across iframe boundaries, you will need to get a reference to the iframe you want to interact with. </br>
206230
If your iframe is inside of a shadow root you could cuse `querySelectorDeep` to find the iframe, then pass the `contentDocument` into the 2nd argument of `querySelectorDeep` or `querySelectorAllDeep`.
207231

208-
209232
### Chrome downloads page
210233

211-
212234
In the below examples the components being searched for are nested within web components `shadowRoots`.
213235

214236
```javascript
215-
216237
// Download and Paste the lib code in dist into chrome://downloads console to try it out :)
217238

218-
console.log(querySelectorShadowDom.querySelectorAllDeep('downloads-item:nth-child(4) #remove'));
219-
console.log(querySelectorShadowDom.querySelectorAllDeep('#downloads-list .is-active a[href^="https://"]'));
220-
console.log(querySelectorShadowDom.querySelectorDeep('#downloads-list div#title-area + a'));
221-
239+
console.log(
240+
querySelectorShadowDom.querySelectorAllDeep(
241+
"downloads-item:nth-child(4) #remove"
242+
)
243+
);
244+
console.log(
245+
querySelectorShadowDom.querySelectorAllDeep(
246+
'#downloads-list .is-active a[href^="https://"]'
247+
)
248+
);
249+
console.log(
250+
querySelectorShadowDom.querySelectorDeep("#downloads-list div#title-area + a")
251+
);
222252
```
223253

224-
225254
# Shady DOM
255+
226256
If using the polyfills and shady DOM, this library will still work.
227257

228258
## Importing
259+
229260
- Shipped as an ES6 module to be included using a bundler of your choice (or not).
230261
- ES5 version bundled ontop the window as `window.querySelectorShadowDom` available for easy include into a test framework
231262

232263
## Running the code locally
264+
233265
`npm install`
266+
234267
### Running the tests
268+
235269
`npm test`
270+
236271
### Running the tests in watch mode
272+
237273
`npm run watch`
238274

239275
### Running the build
240-
`npm run build`
241276

277+
`npm run build`

0 commit comments

Comments
 (0)