@vibe/testkit
Version:
Vibe e2e testing toolkit
379 lines • 17.1 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { test } from "@playwright/test";
/**
* Class representing a base element for Playwright tests.
*/
export class BaseElement {
/**
* Create a BaseElement.
* @param {Page} page - The Playwright page object.
* @param {Locator} locator - The locator for the element.
* @param {string} elementReportName - The name for reporting purposes.
*/
constructor(page, locator, elementReportName) {
this.DEFAULT_TIMEOUT = 30000; // 30 seconds
this.page = page;
this.locator = locator;
this.elementReportName = elementReportName;
}
/**
* Get the page object.
* @returns {Page} - The page object.
*/
getPage() {
return this.page;
}
/**
* Get the locator of the element.
* @returns {Locator} - The locator of the element.
*/
getLocator() {
return this.locator;
}
/**
* Get the element report name.
* @returns {string} - The element report name.
*/
getElementReportName() {
return this.elementReportName;
}
/**
* Hover the element.
* @returns {Promise<void>}
*/
hover() {
return __awaiter(this, void 0, void 0, function* () {
yield test.step(`Hover ${this.getElementReportName()}`, () => __awaiter(this, void 0, void 0, function* () {
yield this.getLocator().hover();
}));
});
}
/**
* Click the element.
* @returns {Promise<void>}
*/
click() {
return __awaiter(this, void 0, void 0, function* () {
yield test.step(`Click ${this.getElementReportName()}`, () => __awaiter(this, void 0, void 0, function* () {
yield this.getLocator().click();
}));
});
}
/**
* Get the text of the element.
* @returns {Promise<string>} - The text of the element (empty string if only whitespace or empty, throws if all are null/undefined).
*/
getText() {
return __awaiter(this, void 0, void 0, function* () {
return yield test.step(`Get text of ${this.getElementReportName()}`, () => __awaiter(this, void 0, void 0, function* () {
const texts = [
yield this.getLocator().innerText(),
yield this.getLocator().textContent(),
yield this.getAttributeValue("value")
];
// If all are null/undefined, throw error
if (texts.every(value => value == null || value === undefined)) {
throw new Error(`Could not retrieve text for ${this.getElementReportName()}: all texts are null/undefined`);
}
// Trim all string values, keep null/undefined as is
const trimmed = texts.map(value => (typeof value === "string" ? value.trim() : value));
// Filter out null/undefined values
const nonNullValues = trimmed.filter(value => value != null);
// If all non-null are empty string, return ""
if (nonNullValues.every(value => value === "")) {
return "";
}
// Otherwise, return the first non-empty string by priority
const firstNonEmpty = nonNullValues.find(value => typeof value === "string" && value !== "");
if (typeof firstNonEmpty === "string") {
return firstNonEmpty;
}
// Fallback: if only empty string(s) and null/undefined remain, return ""
return "";
}));
});
}
/**
* Scroll the element into view if needed.
* @returns {Promise<void>}
*/
scrollIntoView() {
return __awaiter(this, void 0, void 0, function* () {
yield test.step(`Scroll ${this.getElementReportName()} into view`, () => __awaiter(this, void 0, void 0, function* () {
yield this.getLocator().scrollIntoViewIfNeeded();
}));
});
}
/**
* Get the value of an attribute of the element.
* @param {string} attributeName - The name of the attribute.
* @param {number} timeout - The timeout for the attribute value. Defaults to 30 seconds.
* @returns {Promise<string | null>} - The value of the attribute.
*/
getAttributeValue(attributeName_1) {
return __awaiter(this, arguments, void 0, function* (attributeName, timeout = this.DEFAULT_TIMEOUT) {
return yield test.step(`Get attribute ${attributeName} of ${this.getElementReportName()}`, () => __awaiter(this, void 0, void 0, function* () {
const attributeValue = yield this.getLocator().getAttribute(attributeName, { timeout });
if (attributeValue === null || attributeValue === undefined) {
return "";
}
return attributeValue;
}));
});
}
/**
* Wait for the element to be visible.
* @param {number} timeout - The timeout for the element to be visible. Defaults to 30 seconds.
* @returns {Promise<void>}
*/
waitForElementToBeVisible() {
return __awaiter(this, arguments, void 0, function* (timeout = this.DEFAULT_TIMEOUT) {
yield test.step(`Wait for ${this.getElementReportName()} to be visible`, () => __awaiter(this, void 0, void 0, function* () {
yield this.getLocator().waitFor({ state: "visible", timeout });
}));
});
}
/**
* Wait for the element to be hidden.
* @param {number} timeout - The timeout for the element to be hidden. Defaults to 30 seconds.
* @returns {Promise<void>}
*/
waitForElementToBeHidden() {
return __awaiter(this, arguments, void 0, function* (timeout = this.DEFAULT_TIMEOUT) {
yield test.step(`Wait for ${this.getElementReportName()} to be hidden`, () => __awaiter(this, void 0, void 0, function* () {
yield this.getLocator().waitFor({ state: "hidden", timeout });
}));
});
}
/**
* Wait for the element to be absent.
* @param {number} timeout - The timeout for the element to be absent. Defaults to 30 seconds.
* @returns {Promise<void>}
*/
waitForElementToBeDetached() {
return __awaiter(this, arguments, void 0, function* (timeout = this.DEFAULT_TIMEOUT) {
yield test.step(`Wait for ${this.getElementReportName()} to be absent`, () => __awaiter(this, void 0, void 0, function* () {
yield this.getLocator().waitFor({ state: "detached", timeout });
}));
});
}
/**
* Wait for the element to be attached.
* @param {number} timeout - The timeout for the element to be attached. Defaults to 30 seconds.
* @returns {Promise<void>}
*/
waitForElementToBeAttached() {
return __awaiter(this, arguments, void 0, function* (timeout = this.DEFAULT_TIMEOUT) {
yield test.step(`Wait for ${this.getElementReportName()} to be attached`, () => __awaiter(this, void 0, void 0, function* () {
yield this.getLocator().waitFor({ state: "attached", timeout });
}));
});
}
/**
* Count the number of elements matching the locator.
* @returns {Promise<number>} - The number of elements matching the locator.
*/
count() {
return __awaiter(this, void 0, void 0, function* () {
return yield test.step(`Count elements matching ${this.getElementReportName()}`, () => __awaiter(this, void 0, void 0, function* () {
return yield this.getLocator().count();
}));
});
}
/**
* Check if the element is enabled.
* @param {number} timeout - The timeout for the element to be enabled. Defaults to 30 seconds.
* @returns {Promise<boolean>} - Returns true if the element is enabled, otherwise false.
*/
isEnabled() {
return __awaiter(this, arguments, void 0, function* (timeout = this.DEFAULT_TIMEOUT) {
return yield test.step(`Check if ${this.getElementReportName()} is enabled`, () => __awaiter(this, void 0, void 0, function* () {
return yield this.getLocator().isEnabled({ timeout });
}));
});
}
/**
* Check if the element is disabled.
* @param {number} timeout - The timeout for the element to be disabled. Defaults to 30 seconds.
* @returns {Promise<boolean>} - Returns true if the element is disabled, otherwise false.
*/
isDisabled() {
return __awaiter(this, arguments, void 0, function* (timeout = this.DEFAULT_TIMEOUT) {
return yield test.step(`Check if ${this.getElementReportName()} is disabled`, () => __awaiter(this, void 0, void 0, function* () {
return yield this.getLocator().isDisabled({ timeout });
}));
});
}
/**
* Check if the element is visible.
* @param {number} timeout - The timeout for the element to be visible. Defaults to 30 seconds.
* @returns {Promise<boolean>} - Returns true if the element is visible, otherwise false.
*/
isVisible() {
return __awaiter(this, arguments, void 0, function* (timeout = this.DEFAULT_TIMEOUT) {
return yield test.step(`Check if ${this.getElementReportName()} is visible`, () => __awaiter(this, void 0, void 0, function* () {
return yield this.getLocator().isVisible({ timeout });
}));
});
}
/**
* Check if the element is hidden.
* @param {number} timeout - The timeout for the element to be hidden. Defaults to 30 seconds.
* @returns {Promise<boolean>} - Returns true if the element is hidden, otherwise false.
*/
isHidden() {
return __awaiter(this, arguments, void 0, function* (timeout = this.DEFAULT_TIMEOUT) {
return yield test.step(`Check if ${this.getElementReportName()} is hidden`, () => __awaiter(this, void 0, void 0, function* () {
return yield this.getLocator().isHidden({ timeout });
}));
});
}
/**
* Remove focus from the element.
* @returns {Promise<void>}
*/
removeFocus() {
return __awaiter(this, void 0, void 0, function* () {
yield test.step(`Remove focus from ${this.getElementReportName()}`, () => __awaiter(this, void 0, void 0, function* () {
yield this.getPage().locator("body").click();
}));
});
}
/**
* Get the computed style of an element.
* @param {string} property - The property to get the computed style of.
* @returns {Promise<string>} - The computed style of the element.
*/
getComputedStyle(property) {
return __awaiter(this, void 0, void 0, function* () {
return yield test.step(`Get computed style of ${property} for ${this.getElementReportName()}`, () => __awaiter(this, void 0, void 0, function* () {
return yield this.getLocator().evaluate((el, property) => window.getComputedStyle(el).getPropertyValue(property), property);
}));
});
}
/**
* Check if the element is expanded.
* @returns {Promise<boolean>} True if the element is expanded, false otherwise.
*/
isExpanded() {
return __awaiter(this, void 0, void 0, function* () {
return yield test.step(`Check if ${this.getElementReportName()} is expanded`, () => __awaiter(this, void 0, void 0, function* () {
return (yield this.getAttributeValue("aria-expanded")) === "true";
}));
});
}
/**
* Check if the element is checked.
* @returns {Promise<boolean>} True if the element is checked, false otherwise.
*/
isChecked() {
return __awaiter(this, void 0, void 0, function* () {
return yield test.step(`Check if ${this.getElementReportName()} is checked`, () => __awaiter(this, void 0, void 0, function* () {
return (yield this.getAttributeValue("aria-checked")) === "true";
}));
});
}
/**
* Check if the element is selected.
* @returns {Promise<boolean>} True if the element is selected, false otherwise.
*/
isSelected() {
return __awaiter(this, void 0, void 0, function* () {
return yield test.step(`Check if ${this.getElementReportName()} is selected`, () => __awaiter(this, void 0, void 0, function* () {
return (yield this.getAttributeValue("aria-selected")) === "true";
}));
});
}
/**
* Wait for the element to be in a certain state.
* @param {Object} options - The options for the wait.
* @returns {Promise<void>}
* @deprecated Use waitForElementToBeVisible, waitForElementToBeHidden, waitForElementToBeDetached, waitForElementToBeAttached instead.
*/
waitFor() {
return __awaiter(this, arguments, void 0, function* (options = {}) {
yield test.step(`Wait for ${this.getElementReportName()}`, () => __awaiter(this, void 0, void 0, function* () {
yield this.getLocator().waitFor(options);
}));
});
}
/**
* Wait for the element to be visible.
* @returns {Promise<void>}
* @deprecated Use waitForElementToBeVisible instead.
*/
waitForVisible() {
return __awaiter(this, void 0, void 0, function* () {
yield test.step(`Wait for ${this.getElementReportName()} to be visible`, () => __awaiter(this, void 0, void 0, function* () {
yield this.getLocator().waitFor({ state: "visible" });
}));
});
}
/**
* Wait for the element to be absent.
* @returns {Promise<void>}
* @deprecated Use waitForElementToBeDetached instead.
*/
waitForAbsence() {
return __awaiter(this, void 0, void 0, function* () {
yield test.step(`Wait for ${this.getElementReportName()} to be absent`, () => __awaiter(this, void 0, void 0, function* () {
yield this.getLocator().waitFor({ state: "detached" });
}));
});
}
/**
* Wait for the list elements to stabilize (i.e., the count of items remains constant for a specified duration).
* @param {Locator} locator - The locator for the elements.
* @returns {Promise<void>}
* @deprecated
*/
waitForAndVerifyElements(locator) {
return __awaiter(this, void 0, void 0, function* () {
yield test.step(`Wait for ${this.getElementReportName()} items to stabilize and verify existence`, () => __awaiter(this, void 0, void 0, function* () {
let previousCount = 0;
let stableCountTime = 0;
const stabilizationTimeMs = 500;
// eslint-disable-next-line no-constant-condition
while (true) {
const currentCount = yield locator.count();
// Verify we have at least one element
if (currentCount === 0) {
yield this.getPage().waitForTimeout(100);
continue;
}
// Check if all elements are visible
const elements = yield locator.all();
const visibleStates = yield Promise.all(elements.map(el => el.isVisible()));
const allVisible = visibleStates.every(state => state === true);
if (!allVisible) {
yield this.getPage().waitForTimeout(100);
continue;
}
if (currentCount === previousCount) {
stableCountTime += 100;
}
else {
stableCountTime = 0;
}
if (stableCountTime >= stabilizationTimeMs) {
break;
}
previousCount = currentCount;
yield this.getPage().waitForTimeout(100);
}
if ((yield locator.count()) === 0) {
throw new Error(`No ${this.getElementReportName()} elements found after stabilization`);
}
}));
});
}
}
//# sourceMappingURL=BaseElement.js.map