Skip to content

Commit

Permalink
Migrate selectors to use css selector instead of xpath
Browse files Browse the repository at this point in the history
* #1334 update image selector

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 make link use css selector

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 update listitem to use css selector

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 update button selector to use css selector

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 update button selector to use css selector

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 update button

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 update selectors to use css

- fileField
- range
- color
- dropdown

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 update radio button

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 update checkbox

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 update timeField

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 update textbox

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 fix tests

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334  update tabel cells

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 fix test

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 move checkbox logic to relavant wrapper

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 cleanup filefield

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 cleanup button

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 cleanup textbox

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 cleanup radiobutton

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 clean up dropdown

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 cleanup range and colot

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 cleanup timefield

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 cleanup dollar,li,link,image

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 cleanup text

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 cleanup tabel cell

Signed-off-by: NivedhaSenthil <[email protected]>

* fix tests #1334

Signed-off-by: NivedhaSenthil <[email protected]>

* Merge branch 'master' into migrate_selectors

Signed-off-by: NivedhaSenthil <[email protected]>

* Revert "Merge branch 'master' into migrate_selectors"

This reverts commit 199e40d8b1363598deb4ba57f38c3eea0c97dc94.

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 cleanup tests

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 cleanup imports

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 remove console log

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334  support node10

Signed-off-by: NivedhaSenthil <[email protected]>

* #1334 revert tablecell to use xpath

Signed-off-by: NivedhaSenthil <[email protected]>

* #1343 cleanup element wrapper factory

Signed-off-by: NivedhaSenthil <[email protected]>

* Bump version to 1.0.14

Signed-off-by: NivedhaSenthil <[email protected]>

Co-authored-by: Vinay Shankar Shukla <[email protected]>
  • Loading branch information
NivedhaSenthil and Vinay Shankar Shukla authored Jul 15, 2020
1 parent 5480a4a commit 7949551
Show file tree
Hide file tree
Showing 38 changed files with 912 additions and 643 deletions.
9 changes: 9 additions & 0 deletions lib/elementSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,14 @@ const $$xpath = async (selector, selectHiddenElements) => {
return selectHiddenElements ? elements : await filterVisibleNodes(elements);
};

const $function = async (callBack, args, selectHiddenElements) => {
const elements = Element.create(
await runtimeHandler.findElements(callBack, args),
runtimeHandler,
);
return selectHiddenElements ? elements : await filterVisibleNodes(elements);
};

const findFirstElement = async (selector, tag) => {
return (await findElements(selector, tag))[0];
};
Expand Down Expand Up @@ -215,6 +223,7 @@ module.exports = {
match,
$$xpath,
$$,
$function,
findFirstElement,
findElements,
isSelector,
Expand Down
72 changes: 72 additions & 0 deletions lib/elementWrapper/buttonWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const { $function, match } = require('../elementSearch');
const ElementWrapper = require('./elementWrapper');
const { getElementGetter, desc } = require('./helper');

function getButtonElementWithLabel(label) {
const buttons = [];
const inputTypes = ['button', 'submit', 'reset', 'image'];
function checkAndPushElement(elem) {
if (
(elem.tagName && elem.tagName.toLowerCase() === 'button') ||
(elem.tagName &&
elem.tagName.toLowerCase() == 'input' &&
elem.type &&
inputTypes.includes(elem.type.toLowerCase()))
) {
buttons.push(elem);
}
}
const matchingLabels = [...document.querySelectorAll('label')].filter((labelElem) => {
return labelElem.innerText.toLowerCase().includes(label.toLowerCase());
});
for (let matchingLabel of matchingLabels) {
const labelFor = matchingLabel.getAttribute('for');
if (labelFor) {
//check label with attribute for
const labelForElement = document.getElementById(labelFor);
checkAndPushElement(labelForElement);
} else {
// check child node of label tag
matchingLabel.childNodes.forEach((elem) => {
checkAndPushElement(elem);
});
}
}
// check inputs matching types and value
const matchingValues = document.querySelectorAll(
`input[type="submit"][value*="${label}"],input[type="image"][value*="${label}"],input[type="reset"][value*="${label}"],input[type="button"][value*="${label}"]`,
);
return buttons.concat([...matchingValues]);
}
class ButtonWrapper extends ElementWrapper {
constructor(attrValuePairs, _options, ...args) {
super('Button', 'label', attrValuePairs, _options, ...args);

this.getByButton = getElementGetter(
this.selector,
async () => match(this.selector.label, this.options).elements('button', 0, 0),
'button',
this.options.selectHiddenElements,
);

this.getByInput = getElementGetter(
this.selector,
async () =>
await $function(
getButtonElementWithLabel,
this.selector.label,
this.options.selectHiddenElements,
),
'input[type="submit"],input[type="reset"],input[type="button"],input[type="image"]',
this.options.selectHiddenElements,
);
this._get = async () => {
const input = await this.getByInput();
if (input.length) {
return input;
}
return this.getByButton();
};
}
}
module.exports = ButtonWrapper;
59 changes: 58 additions & 1 deletion lib/elementWrapper/checkBoxWrapper.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,64 @@
const CheckBox = require('../elements/checkBox');
const { $function } = require('../elementSearch');
const ElementWrapper = require('./elementWrapper');
const { firstElement } = require('./helper');
const { firstElement, getElementGetter } = require('./helper');

function getCheckBoxElementWithLabel(label) {
const checkBoxes = [];
function checkAndPushElement(elem) {
if (
elem.tagName &&
elem.tagName.toLowerCase() == 'input' &&
elem.type &&
elem.type.toLowerCase() === 'checkbox'
) {
checkBoxes.push(elem);
}
}
const matchingLabels = [...document.querySelectorAll('label')].filter((labelElem) => {
return labelElem.innerText.toLowerCase().includes(label.toLowerCase());
});
for (let matchingLabel of matchingLabels) {
const labelFor = matchingLabel.getAttribute('for');
if (labelFor) {
//check label with attribute for
const labelForElement = document.getElementById(labelFor);
checkAndPushElement(labelForElement);
} else {
// check child node of label tag
matchingLabel.childNodes.forEach((elem) => {
checkAndPushElement(elem);
});
}
}
//check label is inlined
const inputRadioElements = [...document.querySelectorAll('input[type="checkbox" i]')];
for (let inputRadioElement of inputRadioElements) {
if (
inputRadioElement.nextSibling.nodeType === Node.TEXT_NODE &&
inputRadioElement.nextSibling.wholeText.includes(label)
) {
checkBoxes.push(inputRadioElement);
}
}
return checkBoxes;
}
class CheckBoxWrapper extends ElementWrapper {
constructor(attrValuePairs, _options, ...args) {
super('Checkbox', 'label', attrValuePairs, _options, ...args);
this._get = getElementGetter(
this.selector,
async () =>
await $function(
getCheckBoxElementWithLabel,
this.selector.label,
this.options.selectHiddenElements,
),
'input[type="checkbox" i]',
this.options.selectHiddenElements,
);
}

async isChecked() {
const elem = await firstElement.apply(this);
return await elem.isChecked();
Expand Down
49 changes: 48 additions & 1 deletion lib/elementWrapper/colorWrapper.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,54 @@
const Color = require('../elements/color');
const ValueWrapper = require('./valueWrapper');
const { firstElement } = require('./helper');
const { $function } = require('../elementSearch');
const { firstElement, getElementGetter } = require('./helper');

function getColorElementWithLabel(label) {
const fileField = [];
function checkAndPushElement(elem) {
if (
elem.tagName &&
elem.tagName.toLowerCase() == 'input' &&
elem.type &&
elem.type.toLowerCase() === 'color'
) {
fileField.push(elem);
}
}
const matchingLabels = [...document.querySelectorAll('label')].filter((labelElem) => {
return labelElem.innerText.toLowerCase().includes(label.toLowerCase());
});
for (let matchingLabel of matchingLabels) {
const labelFor = matchingLabel.getAttribute('for');
if (labelFor) {
//check label with attribute for
const labelForElement = document.getElementById(labelFor);
checkAndPushElement(labelForElement);
} else {
// check child node of label tag
matchingLabel.childNodes.forEach((elem) => {
checkAndPushElement(elem);
});
}
}
return fileField;
}
class ColorWrapper extends ValueWrapper {
constructor(attrValuePairs, _options, ...args) {
super('Color', 'label', attrValuePairs, _options, ...args);
this._get = getElementGetter(
this.selector,
async () =>
await $function(
getColorElementWithLabel,
this.selector.label,
this.options.selectHiddenElements,
),
'input[type="color"]',
this.options.selectHiddenElements,
);
}

async select(value) {
const elem = await firstElement.apply(this);
return await elem.select(value);
Expand Down
18 changes: 18 additions & 0 deletions lib/elementWrapper/dollarWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const { $$xpath, $$ } = require('../elementSearch');
const { handleRelativeSearch } = require('../proximityElementSearch');
const ElementWrapper = require('./elementWrapper');

class DollarWrapper extends ElementWrapper {
constructor(attrValuePairs, _options, ...args) {
super('CustomSelector', 'query', attrValuePairs, _options, ...args);
this._get = async () => {
return await handleRelativeSearch(
await (this.selector.label.startsWith('//') || this.selector.label.startsWith('(')
? $$xpath(this.selector.label, this.options.selectHiddenElements)
: $$(this.selector.label, this.options.selectHiddenElements)),
this.selector.args,
);
};
}
}
module.exports = DollarWrapper;
44 changes: 43 additions & 1 deletion lib/elementWrapper/dropDownWrapper.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,49 @@
const DropDown = require('../elements/dropDown');
const ValueWrapper = require('./valueWrapper');
const { firstElement } = require('./helper');
const { firstElement, getElementGetter } = require('./helper');
const { $function } = require('../elementSearch');

function getDropDownElementWithLabel(label) {
const fileField = [];
function checkAndPushElement(elem) {
if (elem.tagName && elem.tagName.toLowerCase() == 'select') {
fileField.push(elem);
}
}
const matchingLabels = [...document.querySelectorAll('label')].filter((labelElem) => {
return labelElem.innerText.toLowerCase().includes(label.toLowerCase());
});
for (let matchingLabel of matchingLabels) {
const labelFor = matchingLabel.getAttribute('for');
if (labelFor) {
//check label with attribute for
const labelForElement = document.getElementById(labelFor);
checkAndPushElement(labelForElement);
} else {
// check child node of label tag
matchingLabel.childNodes.forEach((elem) => {
checkAndPushElement(elem);
});
}
}
return fileField;
}
class DropDownWrapper extends ValueWrapper {
constructor(attrValuePairs, _options, ...args) {
super('DropDown', 'label', attrValuePairs, _options, ...args);
this._get = getElementGetter(
this.selector,
async () =>
await $function(
getDropDownElementWithLabel,
this.selector.label,
this.options.selectHiddenElements,
),
'select',
this.options.selectHiddenElements,
);
}

async select(value) {
const elem = await firstElement.apply(this);
return await elem.select(value);
Expand Down
17 changes: 9 additions & 8 deletions lib/elementWrapper/elementWrapper.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
const { firstElement } = require('./helper');
const { firstElement, desc, prepareParameters } = require('./helper');

const { descEvent } = require('../eventBus');
let _getIfExists;
let { getIfExists } = require('../elementSearch');
class ElementWrapper {
constructor(get, description, getIfExists) {
this._get = get;
this._description = description;
_getIfExists = getIfExists;
constructor(elementType, query, attrValuePairs, _options, ...args) {
const { selector, options } = prepareParameters(attrValuePairs, _options, ...args);
this.selector = selector;
this.options = options;
this._description = desc(selector, query, elementType, options);
}

async get(retryInterval, retryTimeout) {
Expand Down Expand Up @@ -54,11 +55,11 @@ class ElementWrapper {
return await elem.isDraggable();
}
async elements(retryInterval, retryTimeout) {
return await _getIfExists(this._get, this._description)(null, retryInterval, retryTimeout);
return await getIfExists(this._get, this._description)(null, retryInterval, retryTimeout);
}

async element(index, retryInterval, retryTimeout) {
const results = await _getIfExists(this._get, this._description)(
const results = await getIfExists(this._get, this._description)(
null,
retryInterval,
retryTimeout,
Expand Down
Loading

0 comments on commit 7949551

Please sign in to comment.