UNPKG

@tshifhiwa/ohrm-ui-automation-framework

Version:

Playwright and TypeScript–based test automation framework for validating core UI features and workflows of the OrangeHRM demo application.

342 lines (321 loc) 12.7 kB
import { expect } from "@playwright/test"; import type { Page, Locator } from "@playwright/test"; import { ActionBase } from "./actionBase.js"; import type { AssertionElementState, WaitForElementState } from "../types/actions.type.js"; import logger from "../../../../../utils/logger/loggerManager.js"; export class ElementAssertions extends ActionBase { constructor(page: Page) { super(page); } /** * Retrieves an element property * @param element The element locator * @param callerMethodName The name of the method that called the action * @param propertyType The type of property to retrieve * @param options Optional: options for retrieving the property * @param elementName Optional: the name of the element * @returns The retrieved property value */ async getElementProperty<T>( element: Locator, callerMethodName: string, propertyType: "attribute" | "dimensions" | "visibleText" | "textContent" | "inputValue", options?: { attributeName?: string }, elementName?: string, ): Promise<T> { return this.performAction( async () => { switch (propertyType) { case "attribute": if (!options?.attributeName) { throw new Error("attributeName is required for 'attribute' property type"); } return element.getAttribute(options.attributeName) as unknown as T; case "dimensions": { const boundingBox = await element.boundingBox(); if (!boundingBox) throw new Error("Failed to get element bounding box"); return { width: boundingBox.width, height: boundingBox.height, } as unknown as T; } case "visibleText": return element.innerText() as unknown as T; case "textContent": return element.textContent() as unknown as T; case "inputValue": return element.inputValue() as unknown as T; default: logger.error(`Unsupported property type: ${propertyType}`); throw new Error(`Unsupported property type: ${propertyType}`); } }, callerMethodName, `Retrieved ${propertyType} from ${elementName}`, `Failed to get ${propertyType} from ${elementName}`, ); } /** * Retrieves all text content from multiple elements * @param elements The Locator of elements to retrieve text content from * @param callerMethodName The name of the method that called the action * @param elementName Optional: the name of the elements * @returns An array of text content from all matching elements */ async getAllTextContents( elements: Locator, callerMethodName: string, elementName?: string, ): Promise<string[]> { return this.performAction( async () => { return elements.allTextContents(); }, callerMethodName, `Retrieved all text contents from ${elementName ?? "elements"}`, `Failed to get all text contents from ${elementName ?? "elements"}`, ); } /** * Checks if an element is visible. * @param element The Locator of the element to check. * @param callerMethodName The name of the method that called the action. * @param elementName Optional: the name of the element. * @returns A promise that resolves with true if the element is visible, or false otherwise. */ async isElementVisible( element: Locator, callerMethodName: string, elementName?: string, ): Promise<boolean> { return this.performAction( () => element.isVisible(), callerMethodName, `Verified: ${elementName} is visible`, `Failed to check visibility of ${elementName}`, ); } /** * Retrieves the count of matching elements. * @param element The Locator of elements to retrieve the count from. * @param callerMethodName The name of the method that called the action. * @param elementName Optional: the name of the elements. * @returns A promise that resolves with the count of matching elements. */ async getElementCount( element: Locator, callerMethodName: string, elementName?: string, ): Promise<number> { return this.performAction( () => element.count(), callerMethodName, `Retrieved count for ${elementName}`, `Failed to get count for ${elementName}`, ); } /** * Retrieves the bounding box of an element. * @param element The Locator of the element to retrieve the bounding box from. * @param callerMethodName The name of the method that called the action. * @param elementName Optional: the name of the element. * @returns A promise that resolves with the bounding box of the element if it succeeds, or null if it fails. * The bounding box is represented as an object with the following properties: * - x: The x-coordinate of the top-left corner of the bounding box. * - y: The y-coordinate of the top-left corner of the bounding box. * - width: The width of the bounding box. * - height: The height of the bounding box. */ async getBoundingBox( element: Locator, callerMethodName: string, elementName?: string, ): Promise<{ x: number; y: number; width: number; height: number } | null> { return this.performAction( () => element.boundingBox(), callerMethodName, `Retrieved bounding box for ${elementName}`, `Failed to get bounding box for ${elementName}`, ); } /** * Verifies that an element is in a specified state. * @param element The Locator of the element to verify the state of. * @param callerMethodName The name of the method that called the action. * @param state The desired state of the element: "enabled", "disabled", "visible", or "hidden". * @param elementName Optional: the name of the element. */ async verifyElementState( element: Locator, callerMethodName: string, state: AssertionElementState, elementName?: string, ): Promise<void> { await this.performAction( async () => { switch (state) { case "enabled": await expect(element).toBeEnabled(); break; case "disabled": await expect(element).toBeDisabled(); break; case "visible": await expect(element).toBeVisible(); break; case "hidden": await expect(element).not.toBeVisible(); break; } }, callerMethodName, `${elementName || "element"} state is ${state}`, `Failed to verify element ${elementName || "element"} is ${state}`, ); } /** * Waits for an element to reach a specified state. * @param element The Locator of the element to wait for. * @param callerMethodName The name of the method that called the action. * @param state The desired state of the element: "attached", "detached", "visible", or "hidden". * @param elementName Optional: the name of the element. * @returns A promise that resolves with the result of the wait action if it succeeds, or rejects with the error if it fails. * @example * await waitForElementState(element, "waitForElementState", "visible", "button"); */ async waitForElementState( element: Locator, callerMethodName: string, state: WaitForElementState, elementName?: string, ): Promise<void> { await this.performAction( async () => { await element.waitFor({ state }); }, callerMethodName, `${elementName || "element"} is ${state}`, `Failed waiting for element ${elementName || "element"} to be ${state}`, ); } /** * Checks if an element is editable. * @param element The Locator of the element to check. * @param callerMethodName The name of the method that called the action. * @param elementName Optional: the name of the element. * @returns A promise that resolves with true if the element is editable, or false otherwise. */ async isElementEditable( element: Locator, callerMethodName: string, elementName?: string, ): Promise<boolean> { return this.performAction( () => element.isEditable(), callerMethodName, `${elementName} is editable`, `Failed to check if ${elementName} is editable`, ); } /** * Checks if an element is read-only. * @param element The Locator of the element to check. * @param callerMethodName The name of the method that called the action. * @param elementName Optional: the name of the element. * @returns A promise that resolves with true if the element is read-only, or false otherwise. * An element is considered read-only if it has the "readonly" attribute, is disabled, or is not editable. */ async isElementReadOnly( element: Locator, callerMethodName: string, elementName?: string, ): Promise<boolean> { return this.performAction( async () => { const readOnlyAttribute = await element.getAttribute("readonly"); const disabledAttribute = await element.getAttribute("disabled"); const isEditable = await element.isEditable(); // Element is read-only if it has readonly attribute, is disabled, or is not editable return readOnlyAttribute !== null || disabledAttribute !== null || !isEditable; }, callerMethodName, `${elementName} is read-only`, `Failed to check if ${elementName} is read-only`, ); } /** * Checks if a cell is non-editable. * @param cell The Locator of the cell to check. * @param callerMethodName The name of the method that called the action. * @param elementName Optional: the name of the element. * @returns A promise that resolves with true if the cell is non-editable, or false otherwise. * A cell is considered non-editable if it does not contain any input or textarea elements, or any elements with the "contenteditable" attribute set to true. */ public async isCellNonEditable( cell: Locator, callerMethodName: string, elementName?: string, ): Promise<boolean> { return this.performAction( async () => { // Check if cell contains an input or textarea const hasInput = await cell.locator("input, textarea, [contenteditable='true']").count(); // If no interactive element is present, it's considered non-editable return hasInput === 0; }, callerMethodName, `${elementName ?? "cell"} is non-editable`, `Failed to check if ${elementName ?? "cell"} is non-editable`, ); } /** * Checks if an element is checked. * @param element The Locator of the element to check. * @param callerMethodName The name of the method that called the action. * @param elementName Optional: the name of the element. * @returns A promise that resolves with true if the element is checked, or false otherwise. * @example * await isElementChecked(element, "isElementChecked", "checkbox"); */ async isElementChecked( element: Locator, callerMethodName: string, elementName?: string, ): Promise<boolean> { return this.performAction( () => element.isChecked(), callerMethodName, `${elementName} is checked`, `Failed to check if ${elementName} is checked`, ); } /** * Verifies the state of a checkbox. * @param element The Locator of the element to verify. * @param callerMethodName The name of the method that called the action. * @param isChecked Whether the checkbox should be checked or not. * @param elementName Optional: the name of the element. * @returns A promise that resolves if the verification succeeds, or rejects with an error if it fails. * @example * await verifyCheckboxState(element, "verifyCheckboxState", true, "checkbox"); */ async verifyCheckboxState( element: Locator, callerMethodName: string, isChecked: boolean, elementName?: string, ): Promise<void> { await this.performAction( async () => { if (isChecked) { await expect(element).toBeChecked(); } else { await expect(element).not.toBeChecked(); } }, callerMethodName, `${elementName} is ${isChecked ? "checked" : "unchecked"}`, `Failed to verify ${elementName} is ${isChecked ? "checked" : "unchecked"}`, ); } }