Skip to content

Commit 9e4bee8

Browse files
committed
allow shadow dom for row el
requires: - to use an autonomous custom element named benchmark-row with a shadowRoot containing one HTMLTableRowElement - to add set `useRowShadowRoot` to `true` in "js-framework-benchmark" in the integration's package.json results for partial update, swap rows & remove row seems seems wrong as they are way too low
1 parent b7e743f commit 9e4bee8

File tree

4 files changed

+66
-43
lines changed

4 files changed

+66
-43
lines changed

webdriver-ts/src/common.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export interface FrameworkData {
6363
uri: string;
6464
keyed: boolean;
6565
useShadowRoot: boolean;
66+
useRowShadowRoot: boolean;
6667
issues: number[];
6768
}
6869

@@ -86,7 +87,7 @@ export interface FrameworkId {
8687

8788
abstract class FrameworkVersionInformationValid implements FrameworkId {
8889
public url: string;
89-
constructor(public keyedType: KeyedType, public directory: string, customURL: string|undefined, public useShadowRoot: boolean, public issues: number[]) {
90+
constructor(public keyedType: KeyedType, public directory: string, customURL: string|undefined, public useShadowRoot: boolean, public useRowShadowRoot: boolean, public issues: number[]) {
9091
this.keyedType = keyedType;
9192
this.directory = directory;
9293
this.url = 'frameworks/'+keyedType+'/'+directory + (customURL ? customURL : '');
@@ -95,21 +96,22 @@ abstract class FrameworkVersionInformationValid implements FrameworkId {
9596

9697
export class FrameworkVersionInformationDynamic extends FrameworkVersionInformationValid {
9798
constructor(keyedType: KeyedType, directory: string, public packageNames: string[],
98-
customURL: string|undefined, useShadowRoot: boolean = false, issues: number[]) {
99-
super(keyedType, directory, customURL, useShadowRoot, issues);
99+
customURL: string|undefined, useShadowRoot: boolean = false, useRowShadowRoot: boolean = false, issues: number[]) {
100+
super(keyedType, directory, customURL, useShadowRoot, useRowShadowRoot, issues);
100101
}
101102
}
102103

103104
export class FrameworkVersionInformationStatic extends FrameworkVersionInformationValid {
104-
constructor(keyedType: KeyedType, directory: string, public frameworkVersion: string, customURL: string|undefined, useShadowRoot: boolean = false, issues: number[]) {
105-
super(keyedType, directory, customURL, useShadowRoot, issues);
105+
constructor(keyedType: KeyedType, directory: string, public frameworkVersion: string, customURL: string|undefined, useShadowRoot: boolean = false, useRowShadowRoot: boolean = false, issues: number[]) {
106+
super(keyedType, directory, customURL, useShadowRoot, useRowShadowRoot, issues);
106107
}
107108
getFrameworkData(): FrameworkData {
108109
return {name: this.directory,
109110
fullNameWithKeyedAndVersion: this.directory+(this.frameworkVersion ? '-v'+this.frameworkVersion : '')+'-'+this.keyedType,
110111
uri: this.url,
111112
keyed: this.keyedType === 'keyed',
112113
useShadowRoot: this.useShadowRoot,
114+
useRowShadowRoot: this.useRowShadowRoot,
113115
issues: this.issues
114116
}
115117
}
@@ -164,13 +166,15 @@ async function loadFrameworkInfo(pathInFrameworksDir: string): Promise<Framework
164166
packageJSON['js-framework-benchmark']['frameworkVersionFromPackage'].split(':'),
165167
packageJSON['js-framework-benchmark']['customURL'],
166168
packageJSON['js-framework-benchmark']['useShadowRoot'],
169+
packageJSON['js-framework-benchmark']['useRowShadowRoot'],
167170
packageJSON['js-framework-benchmark']['issues']
168171
);
169172
} else if (typeof packageJSON['js-framework-benchmark']['frameworkVersion'] === 'string') {
170173
return new FrameworkVersionInformationStatic(keyedType, directory,
171174
packageJSON['js-framework-benchmark']['frameworkVersion'],
172175
packageJSON['js-framework-benchmark']['customURL'],
173176
packageJSON['js-framework-benchmark']['useShadowRoot'],
177+
packageJSON['js-framework-benchmark']['useRowShadowRoot'],
174178
packageJSON['js-framework-benchmark']['issues']
175179
);
176180
} else {
@@ -219,6 +223,7 @@ export class PackageVersionInformationResult {
219223
uri: this.framework.url,
220224
keyed: this.framework.keyedType === 'keyed',
221225
useShadowRoot: this.framework.useShadowRoot,
226+
useRowShadowRoot: this.framework.useRowShadowRoot,
222227
issues: this.framework.issues
223228
}
224229
}

webdriver-ts/src/forkedBenchmarkRunner.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {WebDriver, logging} from 'selenium-webdriver'
22
import {BenchmarkType, Benchmark, benchmarks, fileName, LighthouseData} from './benchmarks'
3-
import {setUseShadowRoot, buildDriver} from './webdriverAccess'
3+
import {setUseShadowRoot, buildDriver, setUseRowShadowRoot} from './webdriverAccess'
44

55
const lighthouse = require('lighthouse');
66
const chromeLauncher = require('chrome-launcher');
@@ -361,6 +361,7 @@ async function runCPUBenchmark(framework: FrameworkData, benchmark: Benchmark, b
361361
driver = buildDriver(benchmarkOptions);
362362
for (let i = 0; i <benchmarkOptions.batchSize; i++) {
363363
setUseShadowRoot(framework.useShadowRoot);
364+
setUseRowShadowRoot(framework.useRowShadowRoot);
364365
await driver.get(`http://localhost:${benchmarkOptions.port}/${framework.uri}/index.html`);
365366

366367
// await (driver as any).sendDevToolsCommand('Network.enable');
@@ -418,6 +419,7 @@ async function runMemBenchmark(framework: FrameworkData, benchmark: Benchmark, b
418419
try {
419420
driver = buildDriver(benchmarkOptions);
420421
setUseShadowRoot(framework.useShadowRoot);
422+
setUseRowShadowRoot(framework.useRowShadowRoot);
421423
await driver.get(`http://localhost:${benchmarkOptions.port}/${framework.uri}/index.html`);
422424

423425
await driver.executeScript("console.timeStamp('initBenchmark')");

webdriver-ts/src/isKeyed.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as yargs from 'yargs';
2-
import {buildDriver, setUseShadowRoot, testTextContains, testTextNotContained, testClassContains, testElementLocatedByXpath, testElementNotLocatedByXPath, testElementLocatedById, clickElementById, clickElementByXPath, getTextByXPath, shadowRoot, findByXPath} from './webdriverAccess'
2+
import {buildDriver, setUseShadowRoot, testTextContains, testTextNotContained, testClassContains, testElementLocatedByXpath, testElementNotLocatedByXPath, testElementLocatedById, clickElementById, clickElementByXPath, getTextByXPath, mainRoot, findByXPath, setUseRowShadowRoot} from './webdriverAccess'
33
import {config, FrameworkData, initializeFrameworks, BenchmarkOptions} from './common'
44
import { WebDriver, By, WebElement } from 'selenium-webdriver';
55
import * as R from 'ramda';
@@ -156,33 +156,32 @@ async function assertClassesContained(elem: WebElement, expectedClassNames: stri
156156
}
157157

158158
export async function checkTRcorrect(driver: WebDriver, timeout = config.TIMEOUT): Promise<boolean> {
159-
let elem = await shadowRoot(driver);
160-
let tr = await findByXPath(elem, '//tbody/tr[1000]');
159+
let tr = await findByXPath(driver, '//tbody/tr[1000]');
161160
if (!await assertChildNodes(tr, [ 'td', 'td', 'a', 'td', 'a', 'span', 'td' ], "tr")) {
162161
return false;
163162
}
164163

165164
// first td
166-
let td1 = await findByXPath(elem, '//tbody/tr[1000]/td[1]');
165+
let td1 = await findByXPath(driver, '//tbody/tr[1000]/td[1]');
167166
if (!await assertClassesContained(td1, ["col-md-1"], "first td")) {
168167
return false;
169168
}
170169

171170

172171
// second td
173-
let td2 = await findByXPath(elem, '//tbody/tr[1000]/td[2]');
172+
let td2 = await findByXPath(driver, '//tbody/tr[1000]/td[2]');
174173
if (!await assertClassesContained(td2, ["col-md-4"], "second td")) {
175174
return false;
176175
}
177176

178177
// third td
179-
let td3 = await findByXPath(elem, '//tbody/tr[1000]/td[3]');
178+
let td3 = await findByXPath(driver, '//tbody/tr[1000]/td[3]');
180179
if (!await assertClassesContained(td3, ["col-md-1"], "third td")) {
181180
return false;
182181
}
183182

184183
// span in third td
185-
let span = await findByXPath(elem, '//tbody/tr[1000]/td[3]/a/span');
184+
let span = await findByXPath(driver, '//tbody/tr[1000]/td[3]/a/span');
186185
if (!await assertClassesContained(span, ["glyphicon","glyphicon-remove"], "span in a in third td")) {
187186
return false;
188187
}
@@ -194,7 +193,7 @@ export async function checkTRcorrect(driver: WebDriver, timeout = config.TIMEOUT
194193

195194

196195
// fourth td
197-
let td4 = await findByXPath(elem, '//tbody/tr[1000]/td[4]');
196+
let td4 = await findByXPath(driver, '//tbody/tr[1000]/td[4]');
198197
if (!await assertClassesContained(td4, ["col-md-6"], "fourth td")) {
199198
return false;
200199
}
@@ -204,8 +203,7 @@ export async function checkTRcorrect(driver: WebDriver, timeout = config.TIMEOUT
204203
}
205204

206205
export async function getInnerHTML(driver: WebDriver, xpath: string, timeout = config.TIMEOUT): Promise<string> {
207-
let elem = await shadowRoot(driver);
208-
elem = await findByXPath(elem, xpath);
206+
let elem = await findByXPath(driver, xpath);
209207
return elem.getAttribute("innerHTML");
210208
}
211209

@@ -227,8 +225,9 @@ async function runBench(frameworkNames: string[]) {
227225
for (let i=0;i<runFrameworks.length;i++) {
228226
let driver = await buildDriver(benchmarkOptions);
229227
try {
230-
let framework = runFrameworks[i];
228+
let framework: FrameworkData = runFrameworks[i];
231229
setUseShadowRoot(framework.useShadowRoot);
230+
setUseRowShadowRoot(framework.useRowShadowRoot);
232231
await driver.get(`http://localhost:${config.PORT}/${framework.uri}/index.html`);
233232
await testElementLocatedById(driver, "add");
234233
await clickElementById(driver,'run');

webdriver-ts/src/webdriverAccess.ts

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@ interface PathPart {
99
}
1010

1111
let useShadowRoot = false;
12+
let useRowShadowRoot = false;
1213

1314
export function setUseShadowRoot(val: boolean) {
1415
useShadowRoot = val;
1516
}
1617

18+
export function setUseRowShadowRoot(val: boolean) {
19+
useRowShadowRoot = val;
20+
}
21+
1722
function convertPath(path: string): Array<PathPart> {
1823
let parts = path.split(/\//).filter(v => !!v);
1924
let res: Array<PathPart> = [];
@@ -35,16 +40,36 @@ function convertPath(path: string): Array<PathPart> {
3540
return res;
3641
}
3742

43+
async function shadowRoot(driver: WebDriver, selector: string): Promise<WebElement> {
44+
const el = await driver.findElement(By.css(selector));
45+
return driver.executeScript(`return arguments[0].shadowRoot`, el);
46+
}
47+
3848
// Fake findByXPath for simple XPath expressions to allow usage with shadow dom
39-
export async function findByXPath(node: WebElement, path: string): Promise<WebElement> {
49+
export async function findByXPath(driver: WebDriver, path: string): Promise<WebElement> {
50+
let root = useShadowRoot ? await shadowRoot(driver, 'main-element') : await driver.findElement(By.tagName("body"));
4051
let paths = convertPath(path);
41-
let n = node;
52+
let n = root;
4253
try {
43-
for (let p of paths) {
44-
// n = n.then(nd => nd.findElements(By.tagName(p.tagName))).then(elems => { // costly since it fetches all elements
45-
let elems = await n.findElements(By.css(p.tagName+":nth-child("+(p.index)+")"));
46-
if (elems==null || elems.length==0) { return null};
47-
n = elems[0];
54+
for (let p of paths) {
55+
let elem;
56+
if (useRowShadowRoot && p.tagName === 'tr') {
57+
try {
58+
const shadowHost = await shadowRoot(driver, `benchmark-row:nth-child(${p.index})`);
59+
elem = await shadowHost.findElement(By.tagName('tr'));
60+
if (elem === null) {
61+
return null;
62+
}
63+
} catch {
64+
return null;
65+
}
66+
} else {
67+
let elems = await n.findElements(By.css(p.tagName+":nth-child("+(p.index)+")"));
68+
if (elems==null || elems.length==0) { return null};
69+
elem = elems[0];
70+
}
71+
72+
n = elem;
4873
}
4974
} catch (e) {
5075
//can happen for StaleElementReferenceError
@@ -70,8 +95,7 @@ export async function testTextContains(driver: WebDriver, xpath: string, text: s
7095
return waitForCondition(driver)(`testTextContains ${xpath} ${text}`,
7196
async function(driver) {
7297
try {
73-
let elem = await shadowRoot(driver);
74-
elem = await findByXPath(elem, xpath);
98+
let elem = await findByXPath(driver, xpath);
7599
if (elem==null) return false;
76100
let v = await elem.getText();
77101
return v && v.indexOf(text)>-1;
@@ -85,8 +109,7 @@ export function testTextNotContained(driver: WebDriver, xpath: string, text: str
85109
return waitForCondition(driver)(`testTextNotContained ${xpath} ${text}`,
86110
async function(driver) {
87111
try {
88-
let elem = await shadowRoot(driver);
89-
elem = await findByXPath(elem, xpath);
112+
let elem = await findByXPath(driver, xpath);
90113
if (elem==null) return false;
91114
let v = await elem.getText();
92115
return v && v.indexOf(text)==-1;
@@ -100,8 +123,7 @@ export function testClassContains(driver: WebDriver, xpath: string, text: string
100123
return waitForCondition(driver)(`testClassContains ${xpath} ${text}`,
101124
async function(driver) {
102125
try {
103-
let elem = await shadowRoot(driver);
104-
elem = await findByXPath(elem, xpath);
126+
let elem = await findByXPath(driver, xpath);
105127
if (elem==null) return false;
106128
let v = await elem.getAttribute("class");
107129
return v && v.indexOf(text)>-1;
@@ -115,8 +137,7 @@ export function testElementLocatedByXpath(driver: WebDriver, xpath: string, time
115137
return waitForCondition(driver)(`testElementLocatedByXpath ${xpath}`,
116138
async function(driver) {
117139
try {
118-
let elem = await shadowRoot(driver);
119-
elem = await findByXPath(elem, xpath);
140+
let elem = await findByXPath(driver, xpath);
120141
return elem ? true : false;
121142
} catch(err) {
122143
console.log("ignoring error in testElementLocatedByXpath for xpath = "+xpath,err.toString())
@@ -128,8 +149,7 @@ export function testElementNotLocatedByXPath(driver: WebDriver, xpath: string, t
128149
return waitForCondition(driver)(`testElementNotLocatedByXPath ${xpath}`,
129150
async function(driver) {
130151
try {
131-
let elem = await shadowRoot(driver);
132-
elem = await findByXPath(elem, xpath);
152+
let elem = await findByXPath(driver, xpath);
133153
return elem ? false : true;
134154
} catch(err) {
135155
console.log("ignoring error in testElementNotLocatedByXPath for xpath = "+xpath,err.toString().split("\n")[0]);
@@ -141,7 +161,7 @@ export function testElementLocatedById(driver: WebDriver, id: string, timeout =
141161
return waitForCondition(driver)(`testElementLocatedById ${id}`,
142162
async function(driver) {
143163
try {
144-
let elem = await shadowRoot(driver);
164+
let elem = await mainRoot(driver);
145165
elem = await elem.findElement(By.id(id));
146166
return true;
147167
} catch(err) {
@@ -164,7 +184,7 @@ async function retry<T>(retryCount: number, driver: WebDriver, fun : (driver: W
164184
// No idea how that can be explained
165185
export function clickElementById(driver: WebDriver, id: string) {
166186
return retry(5, driver, async function (driver) {
167-
let elem = await shadowRoot(driver);
187+
let elem = await mainRoot(driver);
168188
elem = await elem.findElement(By.id(id));
169189
await elem.click();
170190
});
@@ -173,8 +193,7 @@ export function clickElementById(driver: WebDriver, id: string) {
173193
export function clickElementByXPath(driver: WebDriver, xpath: string) {
174194
return retry(5, driver, async function(driver, count) {
175195
if (count>1 && config.LOG_DETAILS) console.log("clickElementByXPath ",xpath," attempt #",count);
176-
let elem = await shadowRoot(driver);
177-
elem = await findByXPath(elem, xpath);
196+
let elem = await findByXPath(driver, xpath);
178197
await elem.click();
179198
});
180199
// Stale element possible:
@@ -184,15 +203,13 @@ export function clickElementByXPath(driver: WebDriver, xpath: string) {
184203
export async function getTextByXPath(driver: WebDriver, xpath: string): Promise<string> {
185204
return await retry(5, driver, async function(driver, count) {
186205
if (count>1 && config.LOG_DETAILS) console.log("getTextByXPath ",xpath," attempt #",count);
187-
let elem = await shadowRoot(driver);
188-
elem = await findByXPath(elem, xpath);
206+
let elem = await findByXPath(driver, xpath);
189207
return await elem.getText();
190208
});
191209
}
192210

193-
export async function shadowRoot(driver: WebDriver) : Promise<WebElement> {
194-
return useShadowRoot ? await driver.executeScript('return document.querySelector("main-element").shadowRoot') as WebElement
195-
: await driver.findElement(By.tagName("body"));
211+
export async function mainRoot(driver: WebDriver) : Promise<WebElement> {
212+
return useShadowRoot ? shadowRoot(driver, 'main-element') : driver.findElement(By.tagName("body"));
196213
}
197214

198215
// node_modules\.bin\chromedriver.cmd --verbose --port=9998 --log-path=chromedriver.log

0 commit comments

Comments
 (0)