Skip to content

Commit

Permalink
feat(elementexplorer): highlight elements
Browse files Browse the repository at this point in the history
- Highlight elements by changing css
- Use the rootEl default 'body'. On second attempt, use a default rootEl ''.
  After the attempt is made, reset the rootEl.

closes angular#3465
  • Loading branch information
cnishina committed Sep 19, 2016
1 parent 5034c89 commit a8e5c82
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 26 deletions.
60 changes: 34 additions & 26 deletions lib/browser.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Util from NodeJs
import * as net from 'net';
import {ActionSequence, Capabilities, Command as WdCommand, FileDetector, Options, promise as wdpromise, Session, TargetLocator, TouchSequence, until, WebDriver, WebElement} from 'selenium-webdriver';

import * as url from 'url';
import * as util from 'util';

import {ExplorerScripts} from './debugger/clients/explorerScripts';
import {build$, build$$, ElementArrayFinder, ElementFinder} from './element';
import {ProtractorExpectedConditions} from './expectedConditions';
import {Locator, ProtractorBy} from './locators';
Expand Down Expand Up @@ -373,12 +373,17 @@ export class ProtractorBrowser extends Webdriver {
* available on the page when finding elements or waiting for stability.
* Only compatible with Angular2.
*/
useAllAngular2AppRoots() {
useAllAngular2AppRoots(): void {
// The empty string is an invalid css selector, so we use it to easily
// signal to scripts to not find a root element.
this.rootEl = '';
}

/**
* Set the rootEl
*/
setRootEl(rootEl: string): void { this.rootEl = rootEl; }

/**
* The same as {@code webdriver.WebDriver.prototype.executeScript},
* but with a customized description for debugging.
Expand Down Expand Up @@ -991,17 +996,9 @@ export class ProtractorBrowser extends Webdriver {
[key: string]: any;
}
let context: Context = {require: require};
global.list = (locator: Locator) => {
/* globals browser */
return global.browser.findElements(locator).then(
(arr: webdriver.WebElement[]) => {
let found: string[] = [];
for (let i = 0; i < arr.length; ++i) {
arr[i].getText().then((text: string) => { found.push(text); });
}
return found;
});
};
(<any>global).list = ExplorerScripts.list;
(<any>global).highlight = ExplorerScripts.highlight;

for (let key in global) {
context[key] = global[key];
}
Expand Down Expand Up @@ -1071,9 +1068,20 @@ export class ProtractorBrowser extends Webdriver {
execute_: function(execFn_: Function) {
this.execPromiseResult_ = this.execPromiseError_ = undefined;

this.execPromise_ = this.execPromise_.then(execFn_).then(
(result: Object) => { this.execPromiseResult_ = result; },
(err: Error) => { this.execPromiseError_ = err; });
(<any>global).tempEl = (<any>global).browser.rootEl;
this.execPromise_ =
this.execPromise_.then(execFn_)
.then((result: Object) => { this.execPromiseResult_ = result; })
.catch(() => {
(<any>global).browser.rootEl = '';
execFn_()
.then((result: Object) => {
this.execPromiseResult_ = result;
})
.catch((err: any) => { this.execPromiseError_ = err; });
(<any>global).browser.rootEl = (<any>global).tempEl;
});


// This dummy command is necessary so that the DeferredExecutor.execute
// break point can find something to stop at instead of moving on to the
Expand All @@ -1090,6 +1098,7 @@ export class ProtractorBrowser extends Webdriver {
// Run code through vm so that we can maintain a local scope which is
// isolated from the rest of the execution.
let res = vm_.runInContext(code, sandbox);

if (!webdriver.promise.isPromise(res)) {
res = webdriver.promise.fulfilled(res);
}
Expand All @@ -1100,8 +1109,7 @@ export class ProtractorBrowser extends Webdriver {
} else {
// The '' forces res to be expanded into a string instead of just
// '[Object]'. Then we remove the extra space caused by the ''
// using
// substring.
// using substring.
return util.format.apply(this, ['', res]).substring(1);
}
});
Expand Down Expand Up @@ -1165,16 +1173,16 @@ export class ProtractorBrowser extends Webdriver {
let debuggerClientPath = __dirname + '/debugger/clients/explorer.js';
let onStartFn = () => {
logger.info();
logger.info('------- Element Explorer -------');
logger.info(
'Starting WebDriver debugger in a child process. Element ' +
'Explorer is still beta, please report issues at ' +
'github.com/angular/protractor');
logger.info('------- Element Explorer ---------------------------------');
logger.info('Starting WebDriver debugger in a child process. Element');
logger.info('Explorer is still beta, please report issues at ');
logger.info('github.com/angular/protractor');
logger.info();
logger.info('Type <tab> to see a list of locator strategies.');
logger.info(
'Use the `list` helper function to find elements by strategy:');
logger.info(' e.g., list(by.binding(\'\')) gets all bindings.');
logger.info('Use the `list` and `highlight` helper function to find');
logger.info('elements by strategy:')
logger.info(' e.g., list(by.binding(\'\')) gets text of all bindings.');
logger.info(' e.g., highlight(by.binding(\'\')) highlight html.');
logger.info();
};
this.initDebugger_(debuggerClientPath, onStartFn, opt_debugPort);
Expand Down
88 changes: 88 additions & 0 deletions lib/debugger/clients/explorerScripts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import {promise, WebElement} from 'selenium-webdriver';

import {ProtractorBrowser} from '../../browser';
import {Locator} from '../../locators';
let absoluteXPath = require('./explorerXpath').absoluteXPath;

export class ExplorerScripts {
static list(locator: Locator) {
(<any>global).tempRootEl = (<any>global).browser.rootEl;
return (<any>global)
.browser.findElements(locator)
.then((arr: WebElement[]) => {
let found: string[] = [];
for (let i = 0; i < arr.length; ++i) {
arr[i].getText().then((text: string) => { found.push(text); });
}
return found;
})
.catch((err: Error) => { throw err; });
}

static highlight(locator: Locator) {
let xPaths: string[] = [];
let cssBorders: string[] = [];

return (<any>global)
.browser.findElements(locator)
.then((arr: WebElement[]) => {

for (let i = 0; i < arr.length; ++i) {
ExplorerScripts.getAbsoluteXPath(arr[i]).then(
(result: string) => { xPaths.push(result); });
(arr[i] as any).getCssValue('border').then((val: string) => {
cssBorders.push(val);
});
}
if (arr.length == 0) {
return 'No elements found.';
}

})
.then(() => {

let timesFlashed = 10;
let waitTime = 500;
for (let t = 0; t < timesFlashed; t++) {
let borderValue = '5px dotted rgb(255, 55, 55)';
if (t % 2 == 1) {
borderValue = '5px dotted rgb(255, 180, 55)';
}
for (let i = 0; i < xPaths.length; i++) {
(<any>global)
.browser.executeScript(
'var x = document.evaluate(\'' + xPaths[i] +
'\', document, null, XPathResult.ANY_TYPE, null);' +
'var xx = x.iterateNext(); xx.style.border = \'' +
borderValue + '\';');
}

global.browser.driver.sleep(waitTime);
}

})
.then(() => {

for (let j = 0; j < cssBorders.length; j++) {
(<any>global)
.browser.executeScript(
'var x = document.evaluate(\'' + xPaths[j] +
'\', document, null, XPathResult.ANY_TYPE, null);' +
'var xx = x.iterateNext(); xx.style.border = \'' +
cssBorders[j] + '\';');
}
return 'Highlighting Completed.';

});
};


static getAbsoluteXPath(element: WebElement): promise.Promise<any> {
return (<any>global)
.browser.executeScript(
'var absoluteXPath = ' +
Function.prototype.toString.call(absoluteXPath) +
';\nreturn absoluteXPath(arguments[0]);',
element);
};
}
56 changes: 56 additions & 0 deletions lib/debugger/clients/explorerXpath.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

// jshint browser: true
// jshint shadow: true
exports.absoluteXPath = function(element) {
var comp, comps = [];
var xpath = '';
var getPos = function(element) {
var position = 1, curNode;
if (element.nodeType == Node.ATTRIBUTE_NODE) {
return null;
}
for (curNode = element.previousSibling; curNode; curNode = curNode.previousSibling) {
if (curNode.nodeName == element.nodeName) {
++position;
}
}
return position;
};

if (element instanceof Document) {
return '/';
}

for (; element && !(element instanceof Document);
element = element.nodeType == Node.ATTRIBUTE_NODE ?
element.ownerElement : element.parentNode) {
comp = comps[comps.length] = {};
switch (element.nodeType) {
case Node.TEXT_NODE:
comp.name = 'text()';
break;
case Node.ATTRIBUTE_NODE:
comp.name = '@' + element.nodeName;
break;
case Node.PROCESSING_INSTRUCTION_NODE:
comp.name = 'processing-instruction()';
break;
case Node.COMMENT_NODE:
comp.name = 'comment()';
break;
case Node.ELEMENT_NODE:
comp.name = element.nodeName;
break;
}
comp.position = getPos(element);
}

for (var i = comps.length - 1; i >= 0; i--) {
comp = comps[i];
xpath += '/' + comp.name.toLowerCase();
if (comp.position !== null) {
xpath += '[' + comp.position + ']';
}
}
return xpath;
};

0 comments on commit a8e5c82

Please sign in to comment.