diff --git a/webdriver-ts/src/common.ts b/webdriver-ts/src/common.ts index a958e4cb6..07f4b0cbd 100644 --- a/webdriver-ts/src/common.ts +++ b/webdriver-ts/src/common.ts @@ -63,6 +63,7 @@ export interface FrameworkData { uri: string; keyed: boolean; useShadowRoot: boolean; + useRowShadowRoot: boolean; issues: number[]; } @@ -86,7 +87,7 @@ export interface FrameworkId { abstract class FrameworkVersionInformationValid implements FrameworkId { public url: string; - constructor(public keyedType: KeyedType, public directory: string, customURL: string|undefined, public useShadowRoot: boolean, public issues: number[]) { + constructor(public keyedType: KeyedType, public directory: string, customURL: string|undefined, public useShadowRoot: boolean, public useRowShadowRoot: boolean, public issues: number[]) { this.keyedType = keyedType; this.directory = directory; this.url = 'frameworks/'+keyedType+'/'+directory + (customURL ? customURL : ''); @@ -95,14 +96,14 @@ abstract class FrameworkVersionInformationValid implements FrameworkId { export class FrameworkVersionInformationDynamic extends FrameworkVersionInformationValid { constructor(keyedType: KeyedType, directory: string, public packageNames: string[], - customURL: string|undefined, useShadowRoot: boolean = false, issues: number[]) { - super(keyedType, directory, customURL, useShadowRoot, issues); + customURL: string|undefined, useShadowRoot: boolean = false, useRowShadowRoot: boolean = false, issues: number[]) { + super(keyedType, directory, customURL, useShadowRoot, useRowShadowRoot, issues); } } export class FrameworkVersionInformationStatic extends FrameworkVersionInformationValid { - constructor(keyedType: KeyedType, directory: string, public frameworkVersion: string, customURL: string|undefined, useShadowRoot: boolean = false, issues: number[]) { - super(keyedType, directory, customURL, useShadowRoot, issues); + constructor(keyedType: KeyedType, directory: string, public frameworkVersion: string, customURL: string|undefined, useShadowRoot: boolean = false, useRowShadowRoot: boolean = false, issues: number[]) { + super(keyedType, directory, customURL, useShadowRoot, useRowShadowRoot, issues); } getFrameworkData(): FrameworkData { return {name: this.directory, @@ -110,6 +111,7 @@ export class FrameworkVersionInformationStatic extends FrameworkVersionInformati uri: this.url, keyed: this.keyedType === 'keyed', useShadowRoot: this.useShadowRoot, + useRowShadowRoot: this.useRowShadowRoot, issues: this.issues } } @@ -164,6 +166,7 @@ async function loadFrameworkInfo(pathInFrameworksDir: string): Promise { - let elem = await shadowRoot(driver); - let tr = await findByXPath(elem, '//tbody/tr[1000]'); + let tr = await findByXPath(driver, '//tbody/tr[1000]'); if (!await assertChildNodes(tr, [ 'td', 'td', 'a', 'td', 'a', 'span', 'td' ], "tr")) { return false; } // first td - let td1 = await findByXPath(elem, '//tbody/tr[1000]/td[1]'); + let td1 = await findByXPath(driver, '//tbody/tr[1000]/td[1]'); if (!await assertClassesContained(td1, ["col-md-1"], "first td")) { return false; } // second td - let td2 = await findByXPath(elem, '//tbody/tr[1000]/td[2]'); + let td2 = await findByXPath(driver, '//tbody/tr[1000]/td[2]'); if (!await assertClassesContained(td2, ["col-md-4"], "second td")) { return false; } // third td - let td3 = await findByXPath(elem, '//tbody/tr[1000]/td[3]'); + let td3 = await findByXPath(driver, '//tbody/tr[1000]/td[3]'); if (!await assertClassesContained(td3, ["col-md-1"], "third td")) { return false; } // span in third td - let span = await findByXPath(elem, '//tbody/tr[1000]/td[3]/a/span'); + let span = await findByXPath(driver, '//tbody/tr[1000]/td[3]/a/span'); if (!await assertClassesContained(span, ["glyphicon","glyphicon-remove"], "span in a in third td")) { return false; } @@ -194,7 +193,7 @@ export async function checkTRcorrect(driver: WebDriver, timeout = config.TIMEOUT // fourth td - let td4 = await findByXPath(elem, '//tbody/tr[1000]/td[4]'); + let td4 = await findByXPath(driver, '//tbody/tr[1000]/td[4]'); if (!await assertClassesContained(td4, ["col-md-6"], "fourth td")) { return false; } @@ -204,8 +203,7 @@ export async function checkTRcorrect(driver: WebDriver, timeout = config.TIMEOUT } export async function getInnerHTML(driver: WebDriver, xpath: string, timeout = config.TIMEOUT): Promise { - let elem = await shadowRoot(driver); - elem = await findByXPath(elem, xpath); + let elem = await findByXPath(driver, xpath); return elem.getAttribute("innerHTML"); } @@ -227,8 +225,9 @@ async function runBench(frameworkNames: string[]) { for (let i=0;i { let parts = path.split(/\//).filter(v => !!v); let res: Array = []; @@ -35,16 +40,36 @@ function convertPath(path: string): Array { return res; } +async function shadowRoot(driver: WebDriver, selector: string): Promise { + const el = await driver.findElement(By.css(selector)); + return driver.executeScript(`return arguments[0].shadowRoot`, el); +} + // Fake findByXPath for simple XPath expressions to allow usage with shadow dom -export async function findByXPath(node: WebElement, path: string): Promise { +export async function findByXPath(driver: WebDriver, path: string): Promise { + let root = useShadowRoot ? await shadowRoot(driver, 'main-element') : await driver.findElement(By.tagName("body")); let paths = convertPath(path); - let n = node; + let n = root; try { - for (let p of paths) { - // n = n.then(nd => nd.findElements(By.tagName(p.tagName))).then(elems => { // costly since it fetches all elements - let elems = await n.findElements(By.css(p.tagName+":nth-child("+(p.index)+")")); - if (elems==null || elems.length==0) { return null}; - n = elems[0]; + for (let p of paths) { + let elem; + if (useRowShadowRoot && p.tagName === 'tr') { + try { + const shadowHost = await shadowRoot(driver, `benchmark-row:nth-child(${p.index})`); + elem = await shadowHost.findElement(By.tagName('tr')); + if (elem === null) { + return null; + } + } catch { + return null; + } + } else { + let elems = await n.findElements(By.css(p.tagName+":nth-child("+(p.index)+")")); + if (elems==null || elems.length==0) { return null}; + elem = elems[0]; + } + + n = elem; } } catch (e) { //can happen for StaleElementReferenceError @@ -70,8 +95,7 @@ export async function testTextContains(driver: WebDriver, xpath: string, text: s return waitForCondition(driver)(`testTextContains ${xpath} ${text}`, async function(driver) { try { - let elem = await shadowRoot(driver); - elem = await findByXPath(elem, xpath); + let elem = await findByXPath(driver, xpath); if (elem==null) return false; let v = await elem.getText(); return v && v.indexOf(text)>-1; @@ -85,8 +109,7 @@ export function testTextNotContained(driver: WebDriver, xpath: string, text: str return waitForCondition(driver)(`testTextNotContained ${xpath} ${text}`, async function(driver) { try { - let elem = await shadowRoot(driver); - elem = await findByXPath(elem, xpath); + let elem = await findByXPath(driver, xpath); if (elem==null) return false; let v = await elem.getText(); return v && v.indexOf(text)==-1; @@ -100,8 +123,7 @@ export function testClassContains(driver: WebDriver, xpath: string, text: string return waitForCondition(driver)(`testClassContains ${xpath} ${text}`, async function(driver) { try { - let elem = await shadowRoot(driver); - elem = await findByXPath(elem, xpath); + let elem = await findByXPath(driver, xpath); if (elem==null) return false; let v = await elem.getAttribute("class"); return v && v.indexOf(text)>-1; @@ -115,8 +137,7 @@ export function testElementLocatedByXpath(driver: WebDriver, xpath: string, time return waitForCondition(driver)(`testElementLocatedByXpath ${xpath}`, async function(driver) { try { - let elem = await shadowRoot(driver); - elem = await findByXPath(elem, xpath); + let elem = await findByXPath(driver, xpath); return elem ? true : false; } catch(err) { console.log("ignoring error in testElementLocatedByXpath for xpath = "+xpath,err.toString()) @@ -128,8 +149,7 @@ export function testElementNotLocatedByXPath(driver: WebDriver, xpath: string, t return waitForCondition(driver)(`testElementNotLocatedByXPath ${xpath}`, async function(driver) { try { - let elem = await shadowRoot(driver); - elem = await findByXPath(elem, xpath); + let elem = await findByXPath(driver, xpath); return elem ? false : true; } catch(err) { 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 = return waitForCondition(driver)(`testElementLocatedById ${id}`, async function(driver) { try { - let elem = await shadowRoot(driver); + let elem = await mainRoot(driver); elem = await elem.findElement(By.id(id)); return true; } catch(err) { @@ -164,7 +184,7 @@ async function retry(retryCount: number, driver: WebDriver, fun : (driver: W // No idea how that can be explained export function clickElementById(driver: WebDriver, id: string) { return retry(5, driver, async function (driver) { - let elem = await shadowRoot(driver); + let elem = await mainRoot(driver); elem = await elem.findElement(By.id(id)); await elem.click(); }); @@ -173,8 +193,7 @@ export function clickElementById(driver: WebDriver, id: string) { export function clickElementByXPath(driver: WebDriver, xpath: string) { return retry(5, driver, async function(driver, count) { if (count>1 && config.LOG_DETAILS) console.log("clickElementByXPath ",xpath," attempt #",count); - let elem = await shadowRoot(driver); - elem = await findByXPath(elem, xpath); + let elem = await findByXPath(driver, xpath); await elem.click(); }); // Stale element possible: @@ -184,15 +203,13 @@ export function clickElementByXPath(driver: WebDriver, xpath: string) { export async function getTextByXPath(driver: WebDriver, xpath: string): Promise { return await retry(5, driver, async function(driver, count) { if (count>1 && config.LOG_DETAILS) console.log("getTextByXPath ",xpath," attempt #",count); - let elem = await shadowRoot(driver); - elem = await findByXPath(elem, xpath); + let elem = await findByXPath(driver, xpath); return await elem.getText(); }); } -export async function shadowRoot(driver: WebDriver) : Promise { - return useShadowRoot ? await driver.executeScript('return document.querySelector("main-element").shadowRoot') as WebElement - : await driver.findElement(By.tagName("body")); +export async function mainRoot(driver: WebDriver) : Promise { + return useShadowRoot ? shadowRoot(driver, 'main-element') : driver.findElement(By.tagName("body")); } // node_modules\.bin\chromedriver.cmd --verbose --port=9998 --log-path=chromedriver.log