playwright-elements
Version:
This is Playwright extension.
590 lines (589 loc) • 20.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebElement = void 0;
exports.$ = $;
exports.$getByAltText = $getByAltText;
exports.$getByLabel = $getByLabel;
exports.$getByPlaceholder = $getByPlaceholder;
exports.$getByRole = $getByRole;
exports.$getByTestId = $getByTestId;
exports.$getByText = $getByText;
exports.$getByTitle = $getByTitle;
exports.initDesktopOrMobile = initDesktopOrMobile;
const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
const index_1 = require("./index");
const test_1 = require("@playwright/test");
function extractSelector(pointer) {
return pointer instanceof WebElement ? pointer.selector : pointer;
}
const _expect = test_1.expect;
class WebElement {
_isFrame = false;
_isInFrame = false;
_frameSelector = 'iframe';
_parents = [];
_selector;
_by;
_byOptions;
_hasLocator;
_hasNotLocator;
_hasText;
_hasNotText;
_nth;
_and = [];
_or = [];
constructor(selector, by, options) {
this._selector = selector;
this._by = by;
this._byOptions = options;
}
// page and frame pointers
selectLocatorMethod(element) {
if (!element)
return undefined;
if (typeof element === 'string')
return index_1.BrowserInstance.currentPage.locator(element);
return element.locator;
}
buildLocator(locatorsChain, element) {
const locatorsChainWithIframeType = element._isInFrame ? locatorsChain.frameLocator(element._frameSelector) : locatorsChain;
switch (element._by) {
case By.getByAltText:
locatorsChain = locatorsChainWithIframeType[By.getByAltText](element.narrowSelector, element._byOptions);
break;
case By.getByLabel:
locatorsChain = locatorsChainWithIframeType[By.getByLabel](element.narrowSelector, element._byOptions);
break;
case By.getByPlaceholder:
locatorsChain = locatorsChainWithIframeType[By.getByPlaceholder](element.narrowSelector, element._byOptions);
break;
case By.getByRole:
locatorsChain = locatorsChainWithIframeType[By.getByRole](element.narrowSelector, element._byOptions);
break;
case By.getByTestId:
locatorsChain = locatorsChainWithIframeType[By.getByTestId](element.narrowSelector);
break;
case By.getByText:
locatorsChain = locatorsChainWithIframeType[By.getByText](element.narrowSelector, element._byOptions);
break;
case By.getByTitle:
locatorsChain = locatorsChainWithIframeType[By.getByTitle](element.narrowSelector, element._byOptions);
break;
default:
locatorsChain = locatorsChainWithIframeType.locator(element.narrowSelector, {
hasText: element._hasText,
hasNotText: element._hasNotText,
has: this.selectLocatorMethod(element._hasLocator),
hasNot: this.selectLocatorMethod(element._hasNotLocator)
});
break;
}
for (const andElement of element._and) {
locatorsChain = locatorsChain.and(andElement instanceof WebElement ?
andElement.locator : locatorsChainWithIframeType.locator(andElement));
}
for (const andElement of element._or) {
locatorsChain = locatorsChain.or(andElement instanceof WebElement ?
andElement.locator : locatorsChainWithIframeType.locator(andElement));
}
if (element._nth != undefined)
locatorsChain = locatorsChain.nth(element._nth);
return locatorsChain;
}
buildParentLocatorsChain() {
let locatorsChain = index_1.BrowserInstance.currentPage;
if (this.parentElements.length > 0) {
let isInFrame = false;
let frameSelector;
for (const element of this.parentElements) {
if (element._isFrame) {
isInFrame = true;
frameSelector = element.narrowSelector;
continue;
}
if (isInFrame) {
element._isInFrame = true;
if (frameSelector)
element._frameSelector = frameSelector;
isInFrame = false;
frameSelector = undefined;
}
locatorsChain = this.buildLocator(locatorsChain, element);
}
if (isInFrame) {
this._isInFrame = true;
if (frameSelector)
this._frameSelector = frameSelector;
}
}
return locatorsChain;
}
get locator() {
return this.buildLocator(this.buildParentLocatorsChain(), this);
}
get _() {
return this.locator;
}
static useExpect(expect) {
expect;
}
expect(message) {
return _expect(this.locator, message);
}
softExpect(message) {
return _expect.soft(this.locator, message);
}
// augmentation
recursiveParentSelectorInjection(element) {
const entries = Object.entries(element)
.filter(([key, value]) => {
if (key == '_hasLocator' || key == '_hasNotLocator')
return false;
return value instanceof WebElement;
});
const values = entries.map(entry => entry[1]);
if (values.length) {
values.forEach((value) => {
value.addParentSelector(this);
const parents = this.parentElements;
for (const parent of parents) {
value.addParentSelector(parent);
}
this.recursiveParentSelectorInjection(value);
});
}
}
subElements(augment) {
const elements = augment;
Object.entries(elements).forEach(([key, value]) => {
const clone = (0, lodash_clonedeep_1.default)(value);
clone.addParentSelector(this);
const parents = this.parentElements;
for (const parent of parents) {
clone.addParentSelector(parent);
}
this.recursiveParentSelectorInjection(clone);
this[key] = clone;
});
return this;
}
withMethods(augment) {
const methods = augment;
Object.keys(methods).forEach(key => {
if (key in this)
throw new Error(`Can not add method with name '${key}' because such method already exists.`);
this[key] = methods[key];
});
return this;
}
with(augment) {
const elements = Object.fromEntries(Object.entries(augment)
.filter(e => e[1] instanceof WebElement));
const functions = Object.fromEntries(Object.entries(augment)
.filter(e => e[1] instanceof Function));
if (Object.keys(elements).length !== 0)
this.subElements(elements);
if (Object.keys(functions).length !== 0)
this.withMethods(functions);
return this;
}
// getters setters
get narrowSelector() {
return this._selector;
}
buildNarrowSelectorWithInternalLocator(target = this) {
return target._hasLocator ?
`${target.narrowSelector} >> internal:has="${typeof target._hasLocator === 'string' ? target._hasLocator : target._hasLocator.narrowSelector}"`
: target.narrowSelector;
}
get selector() {
if (this.parentElements.length)
return `${this.parentsSelector} >> ${this.buildNarrowSelectorWithInternalLocator()}`;
else
return this.buildNarrowSelectorWithInternalLocator();
}
get parentsSelector() {
return this.parentElements.map(element => this.buildNarrowSelectorWithInternalLocator(element)).join(' >> ');
}
parent() {
return this._parents.at(-1);
}
get parentElements() {
return this._parents;
}
addParentSelector(parent) {
this._parents.unshift(parent);
}
// chainable web element creation
clone(options) {
return Object.defineProperties((0, lodash_clonedeep_1.default)(this), {
_selector: {
value: options?.selector ?? this._selector,
writable: false,
configurable: false
},
_hasLocator: {
value: options?.hasLocator ?? this._hasLocator,
writable: true,
configurable: false
},
_hasNotLocator: {
value: options?.hasNotLocator ?? this._hasNotLocator,
writable: true,
configurable: false
},
_hasText: {
value: options?.hasText ?? this._hasText,
writable: true,
configurable: false
},
_hasNotText: {
value: options?.hasNotText ?? this._hasNotLocator,
writable: true,
configurable: false
},
_nth: {
value: options?.nth ?? this._nth,
writable: true,
configurable: false
}
});
}
and(element) {
const clone = this.clone();
clone._and.push(element);
return clone;
}
or(element) {
const clone = this.clone();
clone._or.push(element);
return clone;
}
has(element) {
if (this._by)
throw Error(`has option can not be used with ${this._by}, it can be used only with $ or new WebElement('#id') syntax.`);
return this.clone({
selector: this.narrowSelector,
hasLocator: element,
hasNotLocator: this._hasNotLocator,
hasText: this._hasText,
hasNotText: this._hasNotText,
nth: this._nth
});
}
hasNot(element) {
if (this._by)
throw Error(`hasNot option can not be used with ${this._by}, it can be used only with $ or new WebElement('#id') syntax.`);
return this.clone({
selector: this.narrowSelector,
hasLocator: this._hasLocator,
hasNotLocator: element,
hasText: this._hasText,
hasNotText: this._hasNotText,
nth: this._nth
});
}
hasText(text) {
if (this._by)
throw Error(`has option can not be used with ${this._by}, it can be used only with $ or new WebElement('#id') syntax.`);
return this.clone({
selector: this.narrowSelector,
hasLocator: this._hasLocator,
hasNotLocator: this._hasNotLocator,
hasText: text,
hasNotText: this._hasNotText,
nth: this._nth
});
}
hasNotText(text) {
if (this._by)
throw Error(`hasNot option can not be used with ${this._by}, it can be used only with $ or new WebElement('#id') syntax.`);
return this.clone({
selector: this.narrowSelector,
hasLocator: this._hasLocator,
hasNotLocator: this._hasNotLocator,
hasText: this._hasText,
hasNotText: text,
nth: this._nth
});
}
addParentsToWebElement(element) {
element.addParentSelector(this);
for (const parent of (0, lodash_clonedeep_1.default)(this.parentElements).reverse()) {
element.addParentSelector(parent);
}
return element;
}
contentFrame() {
this._isFrame = true;
return this;
}
owner() {
this._isFrame = false;
return this;
}
addHandler(handler, options) {
const callBack = async () => {
await handler(this);
};
return index_1.BrowserInstance.currentPage.addLocatorHandler(this.locator, callBack, options);
}
removeHandler() {
return index_1.BrowserInstance.currentPage.removeLocatorHandler(this.locator);
}
$(selector) {
return this.addParentsToWebElement(new WebElement(selector));
}
$getByAltText(altText, options) {
return this.addParentsToWebElement(new WebElement(altText, By.getByAltText, options));
}
$getByLabel(label, options) {
return this.addParentsToWebElement(new WebElement(label, By.getByLabel, options));
}
$getByPlaceholder(placeholder, options) {
return this.addParentsToWebElement(new WebElement(placeholder, By.getByPlaceholder, options));
}
$getByRole(role, options) {
return this.addParentsToWebElement(new WebElement(role, By.getByRole, options));
}
$getByTestId(testId) {
return this.addParentsToWebElement(new WebElement(testId, By.getByTestId));
}
$getByText(text, options) {
return this.addParentsToWebElement(new WebElement(text, By.getByText, options));
}
$getByTitle(title, options) {
return this.addParentsToWebElement(new WebElement(title, By.getByTitle, options));
}
nth(index) {
return this.clone({
selector: this.narrowSelector,
hasLocator: this._hasLocator,
hasNotLocator: this._hasNotLocator,
hasText: this._hasText,
hasNotText: this._hasNotText,
nth: index
});
}
first() {
return this.nth(0);
}
last() {
return this.nth(-1);
}
// arrays of elements
async getAll() {
const elements = [];
const amount = await this.count();
for (let i = 0; i < amount; i++) {
elements.push(this.nth(i));
}
return elements;
}
async asyncForEach(action) {
const list = await this.getAll();
const promises = [];
for (const ele of list) {
promises.push(action(ele));
}
await Promise.all(promises);
}
async syncForEach(action) {
const list = await this.getAll();
for (const ele of list) {
await action(ele);
}
}
async map(item) {
const list = await this.getAll();
const futureItems = [];
for (const ele of list) {
futureItems.push(Promise.resolve(item(ele)));
}
return Promise.all(futureItems);
}
filter(options) {
return this.clone({
selector: this.narrowSelector,
hasLocator: options.has ? extractSelector(options.has) : undefined,
hasNotLocator: options.hasNot ? extractSelector(options.hasNot) : undefined,
hasText: options.hasText,
hasNotText: options.hasNotText,
});
}
async filterElements(predicate) {
const list = await this.getAll();
const matchedElements = [];
for (const ele of list) {
if (await predicate(ele))
matchedElements.push(ele);
}
return matchedElements;
}
// Locator methods
async allInnerTexts() {
return this.locator.allInnerTexts();
}
async allTextContents() {
return this.locator.allTextContents();
}
async ariaSnapshot(options) {
return this.locator.ariaSnapshot(options);
}
async blur(options) {
await this.locator.blur(options);
}
async boundingBox(options) {
return this.locator.boundingBox(options);
}
async check(options) {
await this.locator.check(options);
}
async clear(options) {
await this.locator.clear(options);
}
async click(options) {
await this.locator.click(options);
}
async count() {
return this.locator.count();
}
async dblclick(options) {
await this.locator.dblclick(options);
}
async dispatchEvent(type, eventInit, options) {
await this.locator.dispatchEvent(type, eventInit, options);
}
async dragTo(target, options) {
await this.locator.dragTo(target instanceof WebElement ? target.locator : target, options);
}
async fill(value, options) {
await this.locator.fill(value, options);
}
async focus(options) {
await this.locator.focus(options);
}
async getAttribute(name, options) {
return this.locator.getAttribute(name, options);
}
async highlight() {
await this.locator.highlight();
}
async hover(options) {
await this.locator.hover(options);
}
async innerHTML(options) {
return this.locator.innerHTML(options);
}
async innerText(options) {
return this.locator.innerText(options);
}
async inputValue(options) {
return this.locator.inputValue(options);
}
async isChecked(options) {
return this.locator.isChecked(options);
}
async isDisabled(options) {
return this.locator.isDisabled(options);
}
async isEditable(options) {
return this.locator.isEditable(options);
}
async isEnabled(options) {
return this.locator.isEnabled(options);
}
async isHidden() {
return this.locator.isHidden();
}
async isVisible(options) {
return this.locator.isVisible(options);
}
async press(key, options) {
await this.locator.press(key, options);
}
async screenshot(options) {
return this.locator.screenshot(options);
}
async scrollIntoViewIfNeeded(options) {
await this.locator.scrollIntoViewIfNeeded(options);
}
async selectOption(values, options) {
return this.locator.selectOption(values, options);
}
async selectText(options) {
await this.locator.selectText(options);
}
async setChecked(checked, options) {
await this.locator.setChecked(checked, options);
}
async setInputFiles(files, options) {
await this.locator.setInputFiles(files, options);
}
async tap(options) {
await this.locator.tap(options);
}
async textContent(options) {
return this.locator.textContent(options);
}
async type(text, options) {
await this.locator.type(text, options);
}
async pressSequentially(text, options) {
await this.locator.pressSequentially(text, options);
}
async uncheck(options) {
await this.locator.uncheck(options);
}
async waitFor(options) {
await this.locator.waitFor(options);
}
// additional methods
async getText(options) {
const text = await this.locator.textContent(options);
if (text)
return text;
throw new Error(`Text content method returned null for selector: "${this.selector}"`);
}
}
exports.WebElement = WebElement;
var By;
(function (By) {
By["getByAltText"] = "getByAltText";
By["getByLabel"] = "getByLabel";
By["getByPlaceholder"] = "getByPlaceholder";
By["getByRole"] = "getByRole";
By["getByTestId"] = "getByTestId";
By["getByText"] = "getByText";
By["getByTitle"] = "getByTitle";
})(By || (By = {}));
function $(selector) {
return new WebElement(selector);
}
function $getByAltText(altText, options) {
return new WebElement(altText, By.getByAltText, options);
}
function $getByLabel(label, options) {
return new WebElement(label, By.getByLabel, options);
}
function $getByPlaceholder(placeholder, options) {
return new WebElement(placeholder, By.getByPlaceholder, options);
}
function $getByRole(role, options) {
return new WebElement(role, By.getByRole, options);
}
function $getByTestId(testId) {
return new WebElement(testId, By.getByTestId);
}
function $getByText(text, options) {
return new WebElement(text, By.getByText, options);
}
function $getByTitle(title, options) {
return new WebElement(title, By.getByTitle, options);
}
function initDesktopOrMobile(desktop, mobile) {
return index_1.BrowserInstance.isContextMobile ? mobile : desktop;
}