From 054837b1ffb089c75bd190db1f3e040f0676eaee Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 11:41:18 +0700 Subject: [PATCH 01/34] Added Base Page --- lib/helpers/BasePage.ts | 443 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 443 insertions(+) create mode 100644 lib/helpers/BasePage.ts diff --git a/lib/helpers/BasePage.ts b/lib/helpers/BasePage.ts new file mode 100644 index 00000000..66d37a91 --- /dev/null +++ b/lib/helpers/BasePage.ts @@ -0,0 +1,443 @@ +import {expect, Locator, Page} from "@playwright/test"; + +/** + * Base page class providing common UI interaction methods. + * All methods follow best practices for reliability: + * - click: Always checks element visibility before clicking + * - enterText: Always clears before filling text + * - select: Waits for element visibility before selecting + * + * @example + * ```typescript + * class MyPage extends BasePage { + * readonly submitBtn: Locator; + * + * constructor(page: Page) { + * super(page); + * this.submitBtn = page.getByRole('button', { name: 'Submit' }); + * } + * + * async submit() { + * await this.click(this.submitBtn); + * } + * } + * ``` + */ +export class BasePage { + readonly page: Page; + + constructor(page: Page) { + this.page = page; + } + + /** + * Clicks an element after verifying it is visible. + * @param locator - The element to click + * @param options - Optional click configuration + */ + async click(locator: Locator, options?: { force?: boolean; timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.click({ force: options?.force }); + } + + /** + * Double-clicks an element after verifying it is visible. + * @param locator - The element to double-click + * @param options - Optional configuration + */ + async doubleClick(locator: Locator, options?: { force?: boolean; timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.dblclick({ force: options?.force }); + } + + /** + * Right-clicks an element after verifying it is visible. + * @param locator - The element to right-click + * @param options - Optional configuration + */ + async rightClick(locator: Locator, options?: { force?: boolean; timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.click({ button: 'right', force: options?.force }); + } + + /** + * Clicks an element using JavaScript (bypasses actionability checks). + * Use when standard click doesn't work due to overlapping elements. + * @param locator - The element to click + */ + async forceClick(locator: Locator): Promise { + await locator.evaluate((el: HTMLElement) => el.click()); + } + + /** + * Enters text into an input field after clearing it. + * Verifies element visibility before interaction. + * @param locator - The input element + * @param text - The text to enter + * @param options - Optional configuration + */ + async enterText( + locator: Locator, + text: string, + options?: { clearFirst?: boolean; verify?: boolean; timeout?: number } + ): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + if (options?.clearFirst !== false) { + await locator.clear(); + } + await locator.fill(text); + if (options?.verify) { + await expect(locator).toHaveValue(text); + } + } + + /** + * Types text character by character (simulates real typing). + * Useful when fill() doesn't trigger necessary events. + * @param locator - The input element + * @param text - The text to type + * @param options - Optional configuration + */ + async typeText( + locator: Locator, + text: string, + options?: { clearFirst?: boolean; delay?: number; timeout?: number } + ): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + if (options?.clearFirst !== false) { + await locator.clear(); + } + await locator.pressSequentially(text, { delay: options?.delay ?? 50 }); + } + + /** + * Clears an input field. + * @param locator - The input element to clear + */ + async clearText(locator: Locator, options?: { timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.clear(); + } + + /** + * Presses a keyboard key while focused on an element. + * @param locator - The element to focus + * @param key - The key to press (e.g., 'Enter', 'Tab', 'Escape') + */ + async pressKey(locator: Locator, key: string, options?: { timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.press(key); + } + + /** + * Selects an option from a dropdown by value. + * @param locator - The select element + * @param value - The option value to select + */ + async selectByValue(locator: Locator, value: string, options?: { timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.selectOption({ value }); + } + + /** + * Selects an option from a dropdown by visible text. + * @param locator - The select element + * @param text - The option text to select + */ + async selectByText(locator: Locator, text: string, options?: { timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.selectOption({ label: text }); + } + + /** + * Selects an option from a dropdown by index. + * @param locator - The select element + * @param index - The option index to select (0-based) + */ + async selectByIndex(locator: Locator, index: number, options?: { timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.selectOption({ index }); + } + + /** + * Selects multiple options from a multi-select dropdown. + * @param locator - The select element + * @param values - Array of option values to select + */ + async selectMultiple(locator: Locator, values: string[], options?: { timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.selectOption(values); + } + + /** + * Checks a checkbox if it's not already checked. + * @param locator - The checkbox element + */ + async check(locator: Locator, options?: { force?: boolean; timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.check({ force: options?.force }); + } + + /** + * Unchecks a checkbox if it's currently checked. + * @param locator - The checkbox element + */ + async uncheck(locator: Locator, options?: { force?: boolean; timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.uncheck({ force: options?.force }); + } + + /** + * Sets a checkbox to a specific state. + * @param locator - The checkbox element + * @param checked - Whether the checkbox should be checked + */ + async setChecked(locator: Locator, checked: boolean, options?: { force?: boolean; timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.setChecked(checked, { force: options?.force }); + } + + /** + * Hovers over an element. + * @param locator - The element to hover over + */ + async hover(locator: Locator, options?: { force?: boolean; timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.hover({ force: options?.force }); + } + + /** + * Focuses on an element. + * @param locator - The element to focus + */ + async focus(locator: Locator, options?: { timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.focus(); + } + + /** + * Hovers over one element and clicks another (for menus that appear on hover). + * @param hoverLocator - The element to hover over + * @param clickLocator - The element to click after hover + */ + async hoverAndClick( + hoverLocator: Locator, + clickLocator: Locator, + options?: { force?: boolean; timeout?: number } + ): Promise { + await expect(hoverLocator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await hoverLocator.hover(); + await expect(clickLocator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await clickLocator.click({ force: options?.force }); + } + + /** + * Waits for an element to be visible. + * @param locator - The element to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForVisible(locator: Locator, timeout?: number): Promise { + await expect(locator).toBeVisible({ timeout: timeout ?? 5000 }); + } + + /** + * Waits for an element to be hidden. + * @param locator - The element to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForHidden(locator: Locator, timeout?: number): Promise { + await expect(locator).toBeHidden({ timeout: timeout ?? 5000 }); + } + + /** + * Waits for an element to be attached to the DOM. + * @param locator - The element to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForAttached(locator: Locator, timeout?: number): Promise { + await locator.waitFor({ state: 'attached', timeout: timeout ?? 5000 }); + } + + /** + * Waits for an element to be detached from the DOM. + * @param locator - The element to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForDetached(locator: Locator, timeout?: number): Promise { + await locator.waitFor({ state: 'detached', timeout: timeout ?? 5000 }); + } + + /** + * Waits for the page to finish loading (network idle). + */ + async waitForPageLoad(): Promise { + await this.page.waitForLoadState('networkidle'); + } + + /** + * Waits for the DOM to be fully loaded. + */ + async waitForDOMContentLoaded(): Promise { + await this.page.waitForLoadState('domcontentloaded'); + } + + /** + * Asserts that an element is visible. + * @param locator - The element to check + * @param isVisible - Whether the element should be visible (default: true) + */ + async isVisible(locator: Locator, isVisible: boolean = true, timeout?: number): Promise { + await expect(locator).toBeVisible({ visible: isVisible, timeout: timeout ?? 5000 }); + } + + /** + * Asserts that an element is enabled. + * @param locator - The element to check + */ + async isEnabled(locator: Locator, timeout?: number): Promise { + await expect(locator).toBeEnabled({ timeout: timeout ?? 5000 }); + } + + /** + * Asserts that an element is disabled. + * @param locator - The element to check + */ + async isDisabled(locator: Locator, timeout?: number): Promise { + await expect(locator).toBeDisabled({ timeout: timeout ?? 5000 }); + } + + /** + * Asserts that an element contains specific text. + * @param locator - The element to check + * @param text - The text to look for + */ + async containsText(locator: Locator, text: string, timeout?: number): Promise { + await expect(locator).toContainText(text, { timeout: timeout ?? 5000 }); + } + + /** + * Asserts that an element has specific text. + * @param locator - The element to check + * @param text - The exact text expected + */ + async hasText(locator: Locator, text: string, timeout?: number): Promise { + await expect(locator).toHaveText(text, { timeout: timeout ?? 5000 }); + } + + /** + * Asserts that an input has a specific value. + * @param locator - The input element to check + * @param value - The expected value + */ + async hasValue(locator: Locator, value: string, timeout?: number): Promise { + await expect(locator).toHaveValue(value, { timeout: timeout ?? 5000 }); + } + + /** + * Asserts that an element has a specific attribute value. + * @param locator - The element to check + * @param name - The attribute name + * @param value - The expected attribute value + */ + async hasAttribute(locator: Locator, name: string, value: string, timeout?: number): Promise { + await expect(locator).toHaveAttribute(name, value, { timeout: timeout ?? 5000 }); + } + + /** + * Asserts that a specific number of elements exist. + * @param locator - The locator to count + * @param count - The expected count + */ + async hasCount(locator: Locator, count: number, timeout?: number): Promise { + await expect(locator).toHaveCount(count, { timeout: timeout ?? 5000 }); + } + + /** + * Gets the text content of an element. + * @param locator - The element to get text from + * @returns The text content + */ + async getText(locator: Locator, options?: { timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + return await locator.textContent() ?? ''; + } + + /** + * Gets the value of an input element. + * @param locator - The input element + * @returns The input value + */ + async getValue(locator: Locator, options?: { timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + return await locator.inputValue(); + } + + /** + * Gets an attribute value from an element. + * @param locator - The element + * @param attributeName - The attribute name + * @returns The attribute value or null + */ + async getAttribute(locator: Locator, attributeName: string, options?: { timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + return await locator.getAttribute(attributeName); + } + + /** + * Checks if an element is currently visible. + * @param locator - The element to check + * @returns True if visible, false otherwise + */ + async checkIsVisible(locator: Locator): Promise { + return await locator.isVisible(); + } + + /** + * Checks if a checkbox is checked. + * @param locator - The checkbox element + * @returns True if checked, false otherwise + */ + async isChecked(locator: Locator): Promise { + return await locator.isChecked(); + } + + /** + * Scrolls an element into view. + * @param locator - The element to scroll to + */ + async scrollIntoView(locator: Locator, options?: { timeout?: number }): Promise { + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await locator.scrollIntoViewIfNeeded(); + } + + /** + * Sets files on a file input element. + * @param locator - The file input element + * @param filePath - Path to the file(s) to set + */ + async setInputFiles(locator: Locator, filePath: string | string[]): Promise { + await locator.setInputFiles(filePath); + } + + /** + * Clears files from a file input. + * @param locator - The file input element + */ + async clearInputFiles(locator: Locator): Promise { + await locator.setInputFiles([]); + } + + /** + * Drags an element and drops it on another element. + * @param source - The element to drag + * @param target - The element to drop on + */ + async dragTo( + source: Locator, + target: Locator, + options?: { sourcePosition?: { x: number; y: number }; targetPosition?: { x: number; y: number } } + ): Promise { + await source.dragTo(target, options); + } + +} From 31a9695e82ff61b0d5bc2a84fc682147c14aaf53 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 11:41:56 +0700 Subject: [PATCH 02/34] Make UiBaseLocator extends BasePage --- lib/helpers/UiBaseLocators.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/helpers/UiBaseLocators.ts b/lib/helpers/UiBaseLocators.ts index 2dc71b7c..33f9f39f 100644 --- a/lib/helpers/UiBaseLocators.ts +++ b/lib/helpers/UiBaseLocators.ts @@ -1,8 +1,8 @@ import {expect, Locator, Page} from "@playwright/test" import {ConstantHelper} from "./ConstantHelper"; +import {BasePage} from "./BasePage"; -export class UiBaseLocators { - public readonly page: Page; +export class UiBaseLocators extends BasePage { public readonly saveBtn: Locator; public readonly chooseBtn: Locator; public readonly submitBtn: Locator; @@ -165,7 +165,7 @@ export class UiBaseLocators { public readonly nextBtn: Locator; constructor(page: Page) { - this.page = page; + super(page); this.saveBtn = page.getByLabel('Save', {exact: true}); this.submitBtn = page.getByLabel('Submit'); this.deleteExactBtn = page.getByRole('button', {name: 'Delete', exact: true}); From eab6e9403ad1c9e0994a67f8c0c6409a0217c5c1 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 12:02:46 +0700 Subject: [PATCH 03/34] Refactor UiBaseLocators to use BasePage methods and time constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add wait-related methods to BasePage (waitForEnabled, waitForText, waitForURL, etc.) - Add timeout and wait constants to ConstantHelper for consistent timing - Refactor UiBaseLocators to use BasePage methods (click, enterText, isVisible, etc.) - Replace hardcoded timeout values with ConstantHelper constants - Export BasePage from index.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- lib/helpers/BasePage.ts | 184 +++++++++++ lib/helpers/ConstantHelper.ts | 19 ++ lib/helpers/UiBaseLocators.ts | 556 +++++++++++++--------------------- lib/helpers/index.ts | 3 +- 4 files changed, 420 insertions(+), 342 deletions(-) diff --git a/lib/helpers/BasePage.ts b/lib/helpers/BasePage.ts index 66d37a91..a4f8569b 100644 --- a/lib/helpers/BasePage.ts +++ b/lib/helpers/BasePage.ts @@ -281,6 +281,190 @@ export class BasePage { await this.page.waitForLoadState('domcontentloaded'); } + /** + * Waits for all network requests to complete. + */ + async waitForLoadState(): Promise { + await this.page.waitForLoadState(); + } + + /** + * Waits for an element to be enabled. + * @param locator - The element to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForEnabled(locator: Locator, timeout?: number): Promise { + await expect(locator).toBeEnabled({ timeout: timeout ?? 5000 }); + } + + /** + * Waits for an element to be disabled. + * @param locator - The element to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForDisabled(locator: Locator, timeout?: number): Promise { + await expect(locator).toBeDisabled({ timeout: timeout ?? 5000 }); + } + + /** + * Waits for an element to contain specific text. + * @param locator - The element to wait for + * @param text - The text to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForText(locator: Locator, text: string, timeout?: number): Promise { + await expect(locator).toContainText(text, { timeout: timeout ?? 5000 }); + } + + /** + * Waits for an input to have a specific value. + * @param locator - The input element to wait for + * @param value - The value to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForValue(locator: Locator, value: string, timeout?: number): Promise { + await expect(locator).toHaveValue(value, { timeout: timeout ?? 5000 }); + } + + /** + * Waits for an element to have a specific attribute value. + * @param locator - The element to wait for + * @param name - The attribute name + * @param value - The expected attribute value + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForAttribute(locator: Locator, name: string, value: string | RegExp, timeout?: number): Promise { + await expect(locator).toHaveAttribute(name, value, { timeout: timeout ?? 5000 }); + } + + /** + * Waits for an element to have a specific CSS class. + * @param locator - The element to wait for + * @param className - The CSS class to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForClass(locator: Locator, className: string | RegExp, timeout?: number): Promise { + await expect(locator).toHaveClass(className, { timeout: timeout ?? 5000 }); + } + + /** + * Waits for a specific number of elements to exist. + * @param locator - The locator to count + * @param count - The expected count + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForCount(locator: Locator, count: number, timeout?: number): Promise { + await expect(locator).toHaveCount(count, { timeout: timeout ?? 5000 }); + } + + /** + * Waits for an element to be editable. + * @param locator - The element to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForEditable(locator: Locator, timeout?: number): Promise { + await expect(locator).toBeEditable({ timeout: timeout ?? 5000 }); + } + + /** + * Waits for an element to be checked. + * @param locator - The checkbox/radio element to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForChecked(locator: Locator, timeout?: number): Promise { + await expect(locator).toBeChecked({ timeout: timeout ?? 5000 }); + } + + /** + * Waits for an element to be unchecked. + * @param locator - The checkbox/radio element to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForUnchecked(locator: Locator, timeout?: number): Promise { + await expect(locator).not.toBeChecked({ timeout: timeout ?? 5000 }); + } + + /** + * Waits for a specific URL or URL pattern. + * @param url - The URL string or regex pattern to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForURL(url: string | RegExp, timeout?: number): Promise { + await this.page.waitForURL(url, { timeout: timeout ?? 30000 }); + } + + /** + * Waits for a navigation to complete. + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForNavigation(timeout?: number): Promise { + await this.page.waitForLoadState('load', { timeout: timeout ?? 30000 }); + } + + /** + * Waits for a specific time (use sparingly, prefer explicit waits). + * @param milliseconds - Time to wait in milliseconds + */ + async waitForTimeout(milliseconds: number): Promise { + await this.page.waitForTimeout(milliseconds); + } + + /** + * Waits for a network request to a specific URL. + * @param urlOrPredicate - URL string, regex, or predicate function + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForRequest(urlOrPredicate: string | RegExp | ((request: any) => boolean), timeout?: number): Promise { + return await this.page.waitForRequest(urlOrPredicate, { timeout: timeout ?? 30000 }); + } + + /** + * Waits for a network response from a specific URL. + * @param urlOrPredicate - URL string, regex, or predicate function + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForResponse(urlOrPredicate: string | RegExp | ((response: any) => boolean), timeout?: number): Promise { + return await this.page.waitForResponse(urlOrPredicate, { timeout: timeout ?? 30000 }); + } + + /** + * Waits for an element to be focused. + * @param locator - The element to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForFocused(locator: Locator, timeout?: number): Promise { + await expect(locator).toBeFocused({ timeout: timeout ?? 5000 }); + } + + /** + * Waits for an element to be empty (no text content). + * @param locator - The element to wait for + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForEmpty(locator: Locator, timeout?: number): Promise { + await expect(locator).toBeEmpty({ timeout: timeout ?? 5000 }); + } + + /** + * Waits for a function to return true. + * @param predicate - Function that returns a boolean or Promise + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForFunction(predicate: () => boolean | Promise, timeout?: number): Promise { + await this.page.waitForFunction(predicate, { timeout: timeout ?? 30000 }); + } + + /** + * Waits for an element to have a specific CSS property value. + * @param locator - The element to wait for + * @param property - The CSS property name + * @param value - The expected CSS property value + * @param timeout - Maximum time to wait in milliseconds + */ + async waitForCSS(locator: Locator, property: string, value: string | RegExp, timeout?: number): Promise { + await expect(locator).toHaveCSS(property, value, { timeout: timeout ?? 5000 }); + } + /** * Asserts that an element is visible. * @param locator - The element to check diff --git a/lib/helpers/ConstantHelper.ts b/lib/helpers/ConstantHelper.ts index db0c4234..243c0a35 100644 --- a/lib/helpers/ConstantHelper.ts +++ b/lib/helpers/ConstantHelper.ts @@ -1,5 +1,24 @@ export class ConstantHelper { + public static readonly timeout = { + short: 1000, + medium: 5000, + long: 10000, + veryLong: 30000, + navigation: 30000, + pageLoad: 60000 + } + + public static readonly wait = { + minimal: 100, + short: 500, + medium: 1000, + long: 2000, + animation: 300, + debounce: 400, + networkIdle: 5000 + } + public static readonly sections = { content: "Content", media: "Media", diff --git a/lib/helpers/UiBaseLocators.ts b/lib/helpers/UiBaseLocators.ts index 33f9f39f..36c36344 100644 --- a/lib/helpers/UiBaseLocators.ts +++ b/lib/helpers/UiBaseLocators.ts @@ -339,7 +339,7 @@ export class UiBaseLocators extends BasePage { async clickActionsMenuForName(name: string) { await expect(this.page.locator('uui-menu-item[label="' + name + '"]').locator('#menu-item').first()).toBeVisible(); // We need to wait for the load to be finished, otherwise we would run into flaky tests - await this.page.waitForTimeout(1000); + await this.page.waitForTimeout(ConstantHelper.wait.medium); await this.page.locator('uui-menu-item[label="' + name + '"]').locator('#menu-item').first().hover({force: true}); await this.page.locator('uui-menu-item[label="' + name + '"] #action-modal').first().click({force: true}); } @@ -379,7 +379,7 @@ export class UiBaseLocators extends BasePage { async reloadTree(treeName: string) { // Waits until the tree item is visible await expect(this.page.getByLabel(treeName, {exact: true})).toBeVisible(); - await this.page.waitForTimeout(500); + await this.page.waitForTimeout(ConstantHelper.wait.short); await this.clickActionsMenuForName(treeName); await this.clickReloadChildrenActionMenuOption(); @@ -387,43 +387,39 @@ export class UiBaseLocators extends BasePage { } async clickReloadButton() { - await expect(this.reloadBtn).toBeVisible(); - await this.reloadBtn.click(); + await this.click(this.reloadBtn); } async clickReloadChildrenButton() { - await expect(this.reloadChildrenBtn).toBeVisible(); - await this.reloadChildrenBtn.click({force: true}); + await this.click(this.reloadChildrenBtn, {force: true}); } async isSuccessStateVisibleForSaveButton(isVisible: boolean = true) { const regex = new RegExp(`^workspace-action:.*Save$`); const saveButtonLocator = this.page.getByTestId(regex); const saveBtn = this.workspaceAction.filter({has: saveButtonLocator}); - await expect(saveBtn.locator(this.successState)).toBeVisible({visible: isVisible, timeout: 10000}); + await expect(saveBtn.locator(this.successState)).toBeVisible({visible: isVisible, timeout: ConstantHelper.timeout.long}); } async clickSaveButton() { - await expect(this.saveBtn).toBeVisible(); - await this.saveBtn.click(); - await this.page.waitForTimeout(500); + await this.click(this.saveBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickChooseButton() { - await expect(this.chooseBtn).toBeVisible(); - await this.chooseBtn.click(); + await this.click(this.chooseBtn); } async clickChooseContainerButton() { - await this.containerChooseBtn.click(); + await this.click(this.containerChooseBtn); } async clickFilterChooseButton() { - await this.filterChooseBtn.click(); + await this.click(this.filterChooseBtn); } async clickRenameFolderThreeDotsButton() { - await this.renameFolderThreeDotsBtn.click(); + await this.click(this.renameFolderThreeDotsBtn); } async clickRenameFolderButton() { @@ -431,208 +427,170 @@ export class UiBaseLocators extends BasePage { } async clickConfirmRenameButton() { - await this.confirmRenameBtn.click(); + await this.click(this.confirmRenameBtn); } async clickUpdateFolderButton() { - await this.updateFolderBtn.click(); + await this.click(this.updateFolderBtn); } async clickUpdateButton() { - await this.updateBtn.click(); + await this.click(this.updateBtn); } async clickSubmitButton() { - await expect(this.submitBtn).toBeVisible(); - await this.submitBtn.click(); + await this.click(this.submitBtn); } async clickConfirmToSubmitButton() { - await this.confirmToSubmitBtn.click(); + await this.click(this.confirmToSubmitBtn); } async clickChangeButton() { - await this.changeBtn.click(); + await this.click(this.changeBtn); } async clickExactLinkWithName(name: string, toForce: boolean = false) { const exactLinkWithNameLocator = this.page.getByRole('link', {name: name, exact: true}); - await expect(exactLinkWithNameLocator).toBeVisible(); - await exactLinkWithNameLocator.click({force: toForce}); + await this.click(exactLinkWithNameLocator, {force: toForce}); } async enterAliasName(aliasName: string) { // Unlocks alias - await this.page.waitForTimeout(500); - await expect(this.aliasLockBtn).toBeVisible(); - await this.aliasLockBtn.click({force: true}); - await this.aliasNameTxt.clear(); - await this.aliasNameTxt.fill(aliasName); + await this.page.waitForTimeout(ConstantHelper.wait.short); + await this.click(this.aliasLockBtn, {force: true}); + await this.enterText(this.aliasNameTxt, aliasName); } async updateIcon(iconName: string) { - await expect(this.iconBtn).toBeVisible(); // Force click is needed - await this.iconBtn.click({force: true}); + await this.click(this.iconBtn, {force: true}); await this.searchForTypeToFilterValue(iconName); await this.clickLabelWithName(iconName, true, true); await this.clickSubmitButton(); } async clickTextButtonWithName(name: string) { - await expect(this.page.getByText(name, {exact: true})).toBeVisible(); - await this.page.getByText(name, {exact: true}).click(); + await this.click(this.page.getByText(name, {exact: true})); } async clickSelectPropertyEditorButton() { - await expect(this.selectPropertyEditorBtn).toBeVisible(); - await this.selectPropertyEditorBtn.click(); + await this.click(this.selectPropertyEditorBtn); } async clickCreateFolderButton() { - await expect(this.createFolderBtn).toBeVisible(); - await this.createFolderBtn.click(); - await this.page.waitForTimeout(500); // Wait for the action to complete + await this.click(this.createFolderBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async enterAPropertyName(name: string) { - await expect(this.propertyNameTxt).toBeVisible(); - await this.propertyNameTxt.fill(name); + await this.enterText(this.propertyNameTxt, name, {clearFirst: false}); } async clickNextPaginationButton() { - await expect(this.nextPaginationBtn).toBeVisible(); - await this.nextPaginationBtn.click(); + await this.click(this.nextPaginationBtn); } - + async clickNextButton(){ - await expect(this.nextBtn).toBeVisible(); - await this.nextBtn.click(); + await this.click(this.nextBtn); } async clickConfirmButton() { - await expect(this.confirmBtn).toBeVisible(); - await this.confirmBtn.click(); + await this.click(this.confirmBtn); } async clickBreadcrumbButton() { - await expect(this.breadcrumbBtn).toBeVisible(); - await this.breadcrumbBtn.click(); + await this.click(this.breadcrumbBtn); } async clickInsertButton() { - await expect(this.insertBtn).toBeVisible(); - await this.insertBtn.click(); + await this.click(this.insertBtn); } async clickConfirmToDeleteButton() { - await expect(this.confirmToDeleteBtn).toBeVisible(); - await this.confirmToDeleteBtn.click(); - await this.page.waitForTimeout(500); // Wait for the action to complete + await this.click(this.confirmToDeleteBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickConfirmCreateFolderButton() { - await expect(this.confirmCreateFolderBtn).toBeVisible(); - await this.confirmCreateFolderBtn.click(); - await this.page.waitForTimeout(500); // Wait for the action to complete + await this.click(this.confirmCreateFolderBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickRemoveExactButton() { - await expect(this.removeExactBtn).toBeVisible(); - await this.removeExactBtn.click(); + await this.click(this.removeExactBtn); } async clickRemoveButtonForName(name: string) { const removeButtonWithNameLocator = this.page.locator('[name="' + name + '"] [label="Remove"]'); - await expect(removeButtonWithNameLocator).toBeVisible(); - await removeButtonWithNameLocator.click(); + await this.click(removeButtonWithNameLocator); } async clickTrashIconButtonForName(name: string) { const trashIconButtonWithNameLocator = this.page.locator('[name="' + name + '"] [name="icon-trash"]'); - await expect(trashIconButtonWithNameLocator).toBeVisible(); - await trashIconButtonWithNameLocator.click(); + await this.click(trashIconButtonWithNameLocator); } async clickRemoveWithName(name: string) { const removeLabelWithNameLocator = this.page.locator('[label="Remove ' + name + '"]'); - await expect(removeLabelWithNameLocator).toBeVisible(); - await removeLabelWithNameLocator.click(); + await this.click(removeLabelWithNameLocator); } async clickDisableButton() { - await expect(this.disableBtn).toBeVisible(); - await this.disableBtn.click(); + await this.click(this.disableBtn); } async clickConfirmDisableButton() { - await expect(this.confirmDisableBtn).toBeVisible(); - await this.confirmDisableBtn.click(); + await this.click(this.confirmDisableBtn); } async clickConfirmRemoveButton() { - await expect(this.confirmToRemoveBtn).toBeVisible(); - await this.confirmToRemoveBtn.click(); + await this.click(this.confirmToRemoveBtn); } async clickEnableButton() { - await expect(this.enableBtn).toBeVisible(); - await this.enableBtn.click(); + await this.click(this.enableBtn); } async clickConfirmEnableButton() { - await expect(this.confirmEnableBtn).toBeVisible(); - await this.confirmEnableBtn.click(); + await this.click(this.confirmEnableBtn); } async insertDictionaryItem(dictionaryName: string) { await this.clickInsertButton(); - await expect(this.insertDictionaryItemBtn).toBeVisible(); - await this.insertDictionaryItemBtn.click(); - await expect(this.page.getByLabel(dictionaryName)).toBeVisible(); - await this.page.getByLabel(dictionaryName).click(); - await expect(this.chooseBtn).toBeVisible(); - await this.chooseBtn.click(); + await this.click(this.insertDictionaryItemBtn); + await this.click(this.page.getByLabel(dictionaryName)); + await this.click(this.chooseBtn); } async addQueryBuilderWithOrderByStatement(propertyAlias: string, isAscending: boolean) { - await expect(this.queryBuilderBtn).toBeVisible({timeout: 10000}); - await this.queryBuilderBtn.click(); - await expect(this.orderByPropertyAliasBtn).toBeVisible(); - await this.orderByPropertyAliasBtn.click(); - // Wait and choose property alias option + await this.click(this.queryBuilderBtn, {timeout: ConstantHelper.timeout.long}); + await this.click(this.orderByPropertyAliasBtn); + // Wait and choose property alias option await this.waitAndSelectQueryBuilderDropDownList(propertyAlias); - await expect(this.orderByPropertyAliasBtn).toBeVisible(); - await this.orderByPropertyAliasBtn.click(); + await this.click(this.orderByPropertyAliasBtn); // Click to ascending button if isAscending is false if (!isAscending) { - await expect(this.ascendingBtn).toBeVisible(); - await this.ascendingBtn.click(); + await this.click(this.ascendingBtn); } } async addQueryBuilderWithWhereStatement(propertyAlias: string, operator: string, constrainValue: string) { - await expect(this.queryBuilderBtn).toBeVisible({timeout: 10000}); - await this.queryBuilderBtn.click(); + await this.click(this.queryBuilderBtn, {timeout: ConstantHelper.timeout.long}); // Wait and choose property alias - await expect(this.wherePropertyAliasBtn).toBeVisible(); - await this.wherePropertyAliasBtn.click(); + await this.click(this.wherePropertyAliasBtn); await this.waitAndSelectQueryBuilderDropDownList(propertyAlias); // Wait and choose operator - await expect(this.whereOperatorBtn).toBeVisible(); - await this.whereOperatorBtn.click(); + await this.click(this.whereOperatorBtn); await this.waitAndSelectQueryBuilderDropDownList(operator); // Wait and choose constrain value and press Enter - await expect(this.whereConstrainValueTxt).toBeVisible(); - await this.whereConstrainValueTxt.clear(); - await this.whereConstrainValueTxt.fill(constrainValue); - await this.whereConstrainValueTxt.press('Enter'); + await this.enterText(this.whereConstrainValueTxt, constrainValue); + await this.pressKey(this.whereConstrainValueTxt, 'Enter'); } async waitAndSelectQueryBuilderDropDownList(option: string) { const ddlOption = this.page.locator('[open]').locator('uui-combobox-list-option').filter({hasText: option}).first(); - await expect(ddlOption).toBeVisible({timeout: 10000}); - await ddlOption.click(); + await this.click(ddlOption, {timeout: ConstantHelper.timeout.long}); } async createFolder(folderName: string) { @@ -649,10 +607,7 @@ export class UiBaseLocators extends BasePage { } async enterFolderName(folderName: string) { - await expect(this.folderNameTxt).toBeVisible(); - await this.folderNameTxt.clear(); - await this.folderNameTxt.fill(folderName); - await expect(this.folderNameTxt).toHaveValue(folderName); + await this.enterText(this.folderNameTxt, folderName, {verify: true}); } async isTextWithExactNameVisible(name: string, isVisible = true) { @@ -660,9 +615,8 @@ export class UiBaseLocators extends BasePage { } async isQueryBuilderCodeShown(code: string) { - await expect(this.queryBuilderShowCode).toBeVisible(); - await this.queryBuilderShowCode.click(); - await expect(this.queryBuilderShowCode).toContainText(code, {timeout: 10000}); + await this.click(this.queryBuilderShowCode); + await this.containsText(this.queryBuilderShowCode, code, 10000); } async deleteFolder() { @@ -671,22 +625,21 @@ export class UiBaseLocators extends BasePage { } async clickDeleteExactButton() { - await expect(this.deleteExactBtn).toBeVisible(); - await this.deleteExactBtn.click(); + await this.click(this.deleteExactBtn); } async isTreeItemVisible(name: string, isVisible = true) { - await expect(this.page.locator('umb-tree-item').locator('[label="' + name + '"]')).toBeVisible({visible: isVisible}); + await this.isVisible(this.page.locator('umb-tree-item').locator('[label="' + name + '"]'), isVisible); } async doesTreeItemHaveTheCorrectIcon(name: string, icon: string) { - return await expect(this.page.locator('umb-tree-item').filter({hasText: name}).locator('umb-icon').locator('[name="' + icon + '"]')).toBeVisible(); + return await this.isVisible(this.page.locator('umb-tree-item').filter({hasText: name}).locator('umb-icon').locator('[name="' + icon + '"]')); } async goToSection(sectionName: string, checkSections = true, skipReload = false) { if (checkSections) { for (let section in ConstantHelper.sections) { - await expect(this.sectionLinks.getByRole('tab', {name: ConstantHelper.sections[section]})).toBeVisible({timeout: 30000}); + await expect(this.sectionLinks.getByRole('tab', {name: ConstantHelper.sections[section]})).toBeVisible({timeout: ConstantHelper.timeout.navigation}); } } @@ -701,8 +654,7 @@ export class UiBaseLocators extends BasePage { async goToSettingsTreeItem(settingsTreeItemName: string) { await this.goToSection(ConstantHelper.sections.settings); - await expect(this.page.getByLabel(settingsTreeItemName, {exact: true})).toBeVisible(); - await this.page.getByLabel(settingsTreeItemName, {exact: true}).click(); + await this.click(this.page.getByLabel(settingsTreeItemName, {exact: true})); } async clickDataElement(elementName: string, options: any = null) { @@ -714,23 +666,21 @@ export class UiBaseLocators extends BasePage { } async isButtonWithNameVisible(name: string) { - await expect(this.page.getByRole('button', {name: name})).toBeVisible(); + await this.isVisible(this.page.getByRole('button', {name: name})); } async clickLabelWithName(name: string, isExact: boolean = true, toForce: boolean = false) { - await expect(this.page.getByLabel(name, {exact: isExact})).toBeVisible(); - await this.page.getByLabel(name, {exact: isExact}).click({force: toForce}); + await this.click(this.page.getByLabel(name, {exact: isExact}), {force: toForce}); } async clickButtonWithName(name: string, isExact: boolean = false) { const exactButtonWithNameLocator = this.page.getByRole('button', {name: name, exact: isExact}); - await expect(exactButtonWithNameLocator).toBeVisible(); // Force click is needed - await exactButtonWithNameLocator.click({force: true}); + await this.click(exactButtonWithNameLocator, {force: true}); } async isSuccessNotificationVisible(isVisible: boolean = true) { - return await expect(this.successNotification.first()).toBeVisible({visible: isVisible, timeout: 10000}); + return await expect(this.successNotification.first()).toBeVisible({visible: isVisible, timeout: ConstantHelper.timeout.long}); } async doesSuccessNotificationsHaveCount(count: number) { @@ -746,101 +696,82 @@ export class UiBaseLocators extends BasePage { } async clickCreateThreeDotsButton() { - await expect(this.createThreeDotsBtn).toBeVisible(); - await this.createThreeDotsBtn.click(); + await this.click(this.createThreeDotsBtn); } async clickCreateButton() { - await expect(this.createBtn).toBeVisible(); - await this.createBtn.click(); + await this.click(this.createBtn); } async clickAddButton() { - await expect(this.addBtn).toBeVisible(); - await this.addBtn.click(); + await this.click(this.addBtn); }; async clickNewFolderThreeDotsButton() { - await expect(this.newFolderThreeDotsBtn).toBeVisible(); - await this.newFolderThreeDotsBtn.click(); + await this.click(this.newFolderThreeDotsBtn); } async clickEditorSettingsButton(index: number = 0) { - await expect(this.editorSettingsBtn.nth(index)).toBeVisible(); - return this.editorSettingsBtn.nth(index).click(); + await this.click(this.editorSettingsBtn.nth(index)); } async enterDescription(description: string) { - await expect(this.enterDescriptionTxt).toBeVisible(); - await this.enterDescriptionTxt.clear(); - await this.enterDescriptionTxt.fill(description); + await this.enterText(this.enterDescriptionTxt, description); } async doesDescriptionHaveValue(value: string, index: number = 0) { - return await expect(this.descriptionBtn.nth(index)).toHaveValue(value); + return await this.hasValue(this.descriptionBtn.nth(index), value); } async clickStructureTab() { - await this.page.waitForTimeout(1000); - await expect(this.structureTabBtn).toBeVisible(); - await this.structureTabBtn.click(); + await this.page.waitForTimeout(ConstantHelper.wait.medium); + await this.click(this.structureTabBtn); } async clickAllowAtRootButton() { - await expect(this.allowAtRootBtn).toBeVisible(); - await this.allowAtRootBtn.click(); + await this.click(this.allowAtRootBtn); } async clickIAmDoneReorderingButton() { - await expect(this.iAmDoneReorderingBtn).toBeVisible(); - await this.iAmDoneReorderingBtn.click(); + await this.click(this.iAmDoneReorderingBtn); } async clickReorderButton() { - await expect(this.reorderBtn).toBeVisible(); - await this.reorderBtn.click(); + await this.click(this.reorderBtn); } async clickLabelAboveButton() { - await expect(this.labelAboveBtn).toBeVisible(); - await this.labelAboveBtn.click(); + await this.click(this.labelAboveBtn); } async clickMandatoryToggle() { - await expect(this.mandatoryToggle).toBeVisible(); - await this.mandatoryToggle.click(); + await this.click(this.mandatoryToggle); } async selectValidationOption(option: string) { - await expect(this.validation).toBeVisible(); - await this.validation.selectOption(option); + await this.selectByValue(this.validation, option); } async enterRegEx(regEx: string) { - await expect(this.regexTxt).toBeVisible(); - await this.regexTxt.fill(regEx); + await this.enterText(this.regexTxt, regEx, {clearFirst: false}); } async enterRegExMessage(regExMessage: string) { - await expect(this.regexMessageTxt).toBeVisible(); - await this.regexMessageTxt.fill(regExMessage); + await this.enterText(this.regexMessageTxt, regExMessage, {clearFirst: false}); } async clickCompositionsButton() { - await expect(this.compositionsBtn).toBeVisible(); - await this.compositionsBtn.click(); + await this.click(this.compositionsBtn); } async clickAddTabButton() { - await expect(this.addTabBtn).toBeVisible(); - await this.addTabBtn.click(); + await this.click(this.addTabBtn); } async enterTabName(tabName: string) { - await expect(this.unnamedTabTxt).toBeVisible(); - await this.page.waitForTimeout(400); - await this.unnamedTabTxt.clear(); - await this.unnamedTabTxt.fill(tabName); + await this.waitForVisible(this.unnamedTabTxt); + await this.page.waitForTimeout(ConstantHelper.wait.debounce); + await this.enterText(this.unnamedTabTxt, tabName); // We use this to make sure the test id is updated await this.page.getByRole('tab', {name: 'Design'}).click(); // We click again to make sure the tab is focused @@ -848,18 +779,16 @@ export class UiBaseLocators extends BasePage { } async searchForTypeToFilterValue(searchValue: string) { - await expect(this.typeToFilterSearchTxt).toBeVisible(); - await this.typeToFilterSearchTxt.fill(searchValue); + await this.enterText(this.typeToFilterSearchTxt, searchValue, {clearFirst: false}); } async addPropertyEditor(propertyEditorName: string, index: number = 0) { - await expect(this.addPropertyBtn.nth(index)).toBeVisible(); - await this.addPropertyBtn.nth(index).click(); + await this.click(this.addPropertyBtn.nth(index)); await this.enterAPropertyName(propertyEditorName); - await expect(this.propertyNameTxt).toHaveValue(propertyEditorName); + await this.hasValue(this.propertyNameTxt, propertyEditorName); await this.clickSelectPropertyEditorButton(); await this.searchForTypeToFilterValue(propertyEditorName); - await this.page.getByText(propertyEditorName, {exact: true}).click(); + await this.click(this.page.getByText(propertyEditorName, {exact: true})); await this.clickSubmitButton(); } @@ -867,121 +796,102 @@ export class UiBaseLocators extends BasePage { await this.clickEditorSettingsButton(); await this.clickChangeButton(); await this.searchForTypeToFilterValue(propertyEditorName); - await this.page.getByText(propertyEditorName, {exact: true}).click(); + await this.click(this.page.getByText(propertyEditorName, {exact: true})); await this.enterAPropertyName(propertyEditorName); await this.clickSubmitButton(); } async enterPropertyEditorDescription(description: string) { - await expect(this.enterPropertyEditorDescriptionTxt).toBeVisible(); - await this.enterPropertyEditorDescriptionTxt.clear(); - await this.enterPropertyEditorDescriptionTxt.fill(description); + await this.enterText(this.enterPropertyEditorDescriptionTxt, description); } async clickAddGroupButton() { - await expect(this.addGroupBtn).toBeVisible(); - await this.addGroupBtn.click(); + await this.click(this.addGroupBtn); } async clickChooseModalButton() { - await expect(this.chooseModalBtn).toBeVisible(); - await this.chooseModalBtn.click(); + await this.click(this.chooseModalBtn); } async enterGroupName(groupName: string, index: number = 0) { const groupNameTxt = this.groupLabel.nth(index); - await expect(groupNameTxt).toBeVisible(); - await groupNameTxt.clear(); - await groupNameTxt.fill(groupName); + await this.enterText(groupNameTxt, groupName); } async isGroupVisible(groupName: string, isVisible = true) { - await expect(this.groupLabel.filter({hasText: groupName})).toBeVisible({visible: isVisible}); + await this.isVisible(this.groupLabel.filter({hasText: groupName}), isVisible); } async doesGroupHaveValue(value: string) { - await expect(this.groupLabel).toBeVisible(); - return await expect(this.groupLabel).toHaveValue(value); + await this.waitForVisible(this.groupLabel); + return await this.hasValue(this.groupLabel, value); } async rename(newName: string) { await this.clickRenameActionMenuOption(); - await expect(this.newNameTxt).toBeVisible(); - await this.newNameTxt.click(); - await this.newNameTxt.clear(); - await this.newNameTxt.fill(newName); - await this.renameModalBtn.click(); - await this.page.waitForTimeout(500); + await this.click(this.newNameTxt); + await this.enterText(this.newNameTxt, newName); + await this.click(this.renameModalBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async isSuccessButtonWithTextVisible(text: string) { - return await expect(this.successState.filter({hasText: text})).toBeVisible(); + return await this.isVisible(this.successState.filter({hasText: text})); } async dragAndDrop(dragFromSelector: Locator, dragToSelector: Locator, verticalOffset: number = 0, horizontalOffset: number = 0, steps: number = 5) { - await expect(dragFromSelector).toBeVisible(); - await expect(dragToSelector).toBeVisible(); + await this.waitForVisible(dragFromSelector); + await this.waitForVisible(dragToSelector); const targetLocation = await dragToSelector.boundingBox(); const elementCenterX = targetLocation!.x + targetLocation!.width / 2; const elementCenterY = targetLocation!.y + targetLocation!.height / 2; - await dragFromSelector.hover(); + await this.hover(dragFromSelector); await this.page.mouse.move(10, 10); - await dragFromSelector.hover(); + await this.hover(dragFromSelector); await this.page.mouse.down(); - await this.page.waitForTimeout(400); + await this.page.waitForTimeout(ConstantHelper.wait.debounce); await this.page.mouse.move(elementCenterX + horizontalOffset, elementCenterY + verticalOffset, {steps: steps}); - await this.page.waitForTimeout(400); + await this.page.waitForTimeout(ConstantHelper.wait.debounce); await this.page.mouse.up(); } async getButtonWithName(name: string) { - await expect(this.page.getByRole('button', {name: name})).toBeVisible(); + await this.waitForVisible(this.page.getByRole('button', {name: name})); return this.page.getByRole('button', {name: name}); } async clickCreateLink() { - await expect(this.createLink).toBeVisible(); - await this.createLink.click(); + await this.click(this.createLink); } async insertSystemFieldValue(fieldValue: string) { await this.clickInsertButton(); - await expect(this.insertValueBtn).toBeVisible(); - await this.insertValueBtn.click(); - await expect(this.chooseFieldDropDown).toBeVisible(); - await this.chooseFieldDropDown.click(); - await expect(this.systemFieldsOption).toBeVisible(); - await this.systemFieldsOption.click(); - await expect(this.chooseFieldValueDropDown).toBeVisible(); - await this.chooseFieldValueDropDown.click(); - await expect(this.page.getByText(fieldValue)).toBeVisible(); - await this.page.getByText(fieldValue).click(); + await this.click(this.insertValueBtn); + await this.click(this.chooseFieldDropDown); + await this.click(this.systemFieldsOption); + await this.click(this.chooseFieldValueDropDown); + await this.click(this.page.getByText(fieldValue)); await this.clickSubmitButton(); } async insertPartialView(partialViewName: string) { await this.clickInsertButton(); - await expect(this.insertPartialViewBtn).toBeVisible(); - await this.insertPartialViewBtn.click(); - await expect(this.page.getByLabel(partialViewName)).toBeVisible(); - await this.page.getByLabel(partialViewName).click(); + await this.click(this.insertPartialViewBtn); + await this.click(this.page.getByLabel(partialViewName)); await this.clickChooseButton(); } async deletePropertyEditorWithName(name: string) { // We need to hover over the Property Editor to make the delete button visible const propertyEditor = this.page.locator('umb-content-type-design-editor-property', {hasText: name}); - await expect(propertyEditor).toBeVisible(); - await propertyEditor.hover(); - await expect(propertyEditor.getByLabel('Delete')).toBeVisible(); + await this.hover(propertyEditor); // Force click is needed - await propertyEditor.getByLabel('Delete').click({force: true}); + await this.click(propertyEditor.getByLabel('Delete'), {force: true}); await this.clickConfirmToDeleteButton(); } async clickRenameButton() { - await expect(this.renameBtn).toBeVisible(); - await this.renameBtn.click(); + await this.click(this.renameBtn); } async clickDeleteAndConfirmButton() { @@ -990,19 +900,16 @@ export class UiBaseLocators extends BasePage { } async clickDeleteButton() { - await expect(this.deleteBtn).toBeVisible(); - await this.deleteBtn.click(); + await this.click(this.deleteBtn); } async clickQueryBuilderButton() { - await expect(this.queryBuilderBtn).toBeVisible(); - await this.queryBuilderBtn.click(); - await this.page.waitForTimeout(500); + await this.click(this.queryBuilderBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async chooseRootContentInQueryBuilder(contentName: string) { - await expect(this.chooseRootContentBtn).toBeVisible(); - await this.chooseRootContentBtn.click(); + await this.click(this.chooseRootContentBtn); await this.clickModalMenuItemWithName(contentName); await this.clickChooseButton(); } @@ -1019,53 +926,47 @@ export class UiBaseLocators extends BasePage { } async clickAllowedChildNodesButton() { - await expect(this.allowedChildNodesModal.locator(this.chooseBtn)).toBeVisible(); - await this.allowedChildNodesModal.locator(this.chooseBtn).click(); + await this.click(this.allowedChildNodesModal.locator(this.chooseBtn)); } async clickAddCollectionButton() { - await expect(this.addCollectionBtn).toBeVisible(); - await this.addCollectionBtn.click(); + await this.click(this.addCollectionBtn); } async doesReturnedItemsHaveCount(itemCount: number) { - await expect(this.returnedItemsCount).toContainText(itemCount.toString() + ' published items returned'); + await this.containsText(this.returnedItemsCount, itemCount.toString() + ' published items returned'); } async doesQueryResultHaveContentName(contentName: string) { - await expect(this.queryResults).toContainText(contentName); + await this.containsText(this.queryResults, contentName); } async deleteGroup(groupName: string) { - await this.page.waitForTimeout(1000); + await this.page.waitForTimeout(ConstantHelper.wait.medium); const groups = this.page.locator('umb-content-type-design-editor-group').all(); for (const group of await groups) { if (await group.getByLabel('Group', {exact: true}).inputValue() === groupName) { const headerActionsDeleteLocator = group.locator('[slot="header-actions"]').getByLabel('Delete'); - await expect(headerActionsDeleteLocator).toBeVisible(); // Force click is needed - await headerActionsDeleteLocator.click({force: true}); + await this.click(headerActionsDeleteLocator, {force: true}); return; } } } async clickRemoveTabWithName(name: string) { - await expect(this.page.locator('uui-tab').filter({hasText: name})).toBeVisible(); - await this.page.locator('uui-tab').filter({hasText: name}).hover(); - const removeTabWithNameLocator = this.page.locator('uui-tab').filter({hasText: name}).locator('[label="Remove"]'); - await expect(removeTabWithNameLocator).toBeVisible(); - await removeTabWithNameLocator.click(); + const tab = this.page.locator('uui-tab').filter({hasText: name}); + await this.hover(tab); + const removeTabWithNameLocator = tab.locator('[label="Remove"]'); + await this.click(removeTabWithNameLocator); } async clickLeftArrowButton() { - await expect(this.leftArrowBtn).toBeVisible(); - await this.leftArrowBtn.click(); + await this.click(this.leftArrowBtn); } async clickToUploadButton() { - await expect(this.clickToUploadBtn).toBeVisible(); - await this.clickToUploadBtn.click(); + await this.click(this.clickToUploadBtn); } async uploadFile(filePath: string) { @@ -1089,27 +990,25 @@ export class UiBaseLocators extends BasePage { }; async isFailedStateButtonVisible() { - await expect(this.failedStateButton).toBeVisible(); + await this.isVisible(this.failedStateButton); } async clickContainerSaveAndPublishButton() { - await expect(this.containerSaveAndPublishBtn).toBeVisible(); - await this.containerSaveAndPublishBtn.click(); - await this.page.waitForTimeout(500); + await this.click(this.containerSaveAndPublishBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickConfirmTrashButton() { - await expect(this.confirmTrashBtn).toBeVisible(); - await this.confirmTrashBtn.click(); - await this.page.waitForTimeout(500); + await this.click(this.confirmTrashBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async reloadRecycleBin(containsItems = true) { - await expect(this.recycleBinMenuItem).toBeVisible(); - // If the Recycle Bin does not contain any items,0 the caret button should not be visible. and we should not try to click it + await this.waitForVisible(this.recycleBinMenuItem); + // If the Recycle Bin does not contain any items, the caret button should not be visible. and we should not try to click it if (!containsItems) { await this.clickReloadChildrenActionMenuOption(); - await expect(this.recycleBinMenuItemCaretBtn).not.toBeVisible(); + await this.isVisible(this.recycleBinMenuItemCaretBtn, false); return; } @@ -1120,31 +1019,28 @@ export class UiBaseLocators extends BasePage { } async clickRecycleBinButton() { - await expect(this.recycleBinBtn).toBeVisible(); - await this.recycleBinBtn.click(); + await this.click(this.recycleBinBtn); } async isItemVisibleInRecycleBin(item: string, isVisible: boolean = true, isReload: boolean = true) { if (isReload) { await this.reloadRecycleBin(isVisible); } - return await expect(this.page.locator('[label="Recycle Bin"] [label="' + item + '"]')).toBeVisible({visible: isVisible}); + return await this.isVisible(this.page.locator('[label="Recycle Bin"] [label="' + item + '"]'), isVisible); } async changeToGridView() { - await expect(this.viewBundleBtn).toBeVisible(); - await this.viewBundleBtn.click(); - await this.gridBtn.click(); + await this.click(this.viewBundleBtn); + await this.click(this.gridBtn); } async changeToListView() { - await expect(this.viewBundleBtn).toBeVisible(); - await this.viewBundleBtn.click(); - await this.listBtn.click(); + await this.click(this.viewBundleBtn); + await this.click(this.listBtn); } async isViewBundleButtonVisible(isVisible: boolean = true) { - return expect(this.viewBundleBtn).toBeVisible({visible: isVisible}); + return this.isVisible(this.viewBundleBtn, isVisible); } async doesSuccessNotificationHaveText(text: string, isVisible: boolean = true, deleteNotification = false, timeout = 5000) { @@ -1167,170 +1063,153 @@ export class UiBaseLocators extends BasePage { } async isSectionWithNameVisible(sectionName: string, isVisible: boolean = true) { - await expect(this.page.getByRole('tab', {name: sectionName})).toBeVisible({visible: isVisible}); + await this.isVisible(this.page.getByRole('tab', {name: sectionName}), isVisible); } async clickMediaWithName(name: string) { - await expect(this.mediaCardItems.filter({hasText: name})).toBeVisible(); - await this.mediaCardItems.filter({hasText: name}).click(); + await this.click(this.mediaCardItems.filter({hasText: name})); } async clickChooseContentStartNodeButton() { - await expect(this.chooseDocumentInputBtn).toBeVisible(); - await this.chooseDocumentInputBtn.click(); + await this.click(this.chooseDocumentInputBtn); } async clickChooseMediaStartNodeButton() { - await expect(this.chooseMediaInputBtn).toBeVisible(); - await this.chooseMediaInputBtn.click(); + await this.click(this.chooseMediaInputBtn); } async clickActionButton() { - await expect(this.actionBtn).toBeVisible(); - await this.actionBtn.click(); + await this.click(this.actionBtn); } async clickReferenceNodeLinkWithName(name: string) { - await expect(this.page.locator('[name="' + name + '"] a#open-part')).toBeVisible(); - await this.page.locator('[name="' + name + '"] a#open-part').click(); + await this.click(this.page.locator('[name="' + name + '"] a#open-part')); } async clickLinkWithName(name: string, isExact: boolean = false) { - await expect(this.page.getByRole('link', {name: name, exact: isExact})).toBeVisible(); - await this.page.getByRole('link', {name: name, exact: isExact}).click(); + await this.click(this.page.getByRole('link', {name: name, exact: isExact})); } async clickMediaPickerModalSubmitButton() { - await expect(this.mediaPickerModalSubmitBtn).toBeVisible(); - await this.mediaPickerModalSubmitBtn.click(); + await this.click(this.mediaPickerModalSubmitBtn); } async selectMediaWithName(mediaName: string, isForce: boolean = false) { const mediaLocator = this.mediaCardItems.filter({hasText: mediaName}); - await expect(mediaLocator).toBeVisible(); + await this.waitForVisible(mediaLocator); await mediaLocator.click({position: {x: 0.5, y: 0.5}, force: isForce}); } async selectMediaWithTestId(mediaKey: string) { const locator = this.page.getByTestId('media:' + mediaKey); - await expect(locator).toBeVisible(); + await this.waitForVisible(locator); await locator.click({position: {x: 0.5, y: 0.5}}); } async clickCreateModalButton() { - await expect(this.createModalBtn).toBeVisible(); - await this.createModalBtn.click(); + await this.click(this.createModalBtn); } async clickMediaCaptionAltTextModalSubmitButton() { - await expect(this.mediaCaptionAltTextModalSubmitBtn).toBeVisible(); - await this.mediaCaptionAltTextModalSubmitBtn.click(); + await this.click(this.mediaCaptionAltTextModalSubmitBtn); } // Embed Modal async enterEmbeddedURL(value: string) { - await expect(this.embeddedURLTxt).toBeVisible(); - await this.embeddedURLTxt.clear(); - await this.embeddedURLTxt.fill(value); + await this.enterText(this.embeddedURLTxt, value); } async clickEmbeddedRetrieveButton() { - await expect(this.embeddedRetrieveBtn).toBeVisible(); - await this.embeddedRetrieveBtn.click(); + await this.click(this.embeddedRetrieveBtn); } async clickEmbeddedMediaModalConfirmButton() { - await expect(this.embeddedMediaModalConfirmBtn).toBeVisible(); - await this.embeddedMediaModalConfirmBtn.click(); + await this.click(this.embeddedMediaModalConfirmBtn); } async waitForEmbeddedPreviewVisible() { - await expect(this.embeddedPreview).toBeVisible(); + await this.waitForVisible(this.embeddedPreview); } async isSubmitButtonDisabled() { - await expect(this.submitBtn).toBeVisible(); - await expect(this.submitBtn).toHaveAttribute('disabled'); + await this.isVisible(this.submitBtn); + await this.isDisabled(this.submitBtn); } async doesMediaHaveThumbnail(mediaId: string, thumbnailIconName: string, thumbnailImage: string) { const mediaThumbnailLocator = this.page.getByTestId('media:' + mediaId); if (thumbnailIconName === 'image') { const regexImageSrc = new RegExp(`^${thumbnailImage}.*`); - await expect(mediaThumbnailLocator.locator('umb-imaging-thumbnail img')).toHaveAttribute('src', regexImageSrc); + await this.hasAttribute(mediaThumbnailLocator.locator('umb-imaging-thumbnail img'), 'src', regexImageSrc.toString()); } else { - await expect(mediaThumbnailLocator.locator('umb-imaging-thumbnail umb-icon')).toHaveAttribute('name', thumbnailIconName); + await this.hasAttribute(mediaThumbnailLocator.locator('umb-imaging-thumbnail umb-icon'), 'name', thumbnailIconName); } } async clickCurrentUserAvatarButton() { - await expect(this.currentUserAvatarBtn).toBeVisible(); - await this.currentUserAvatarBtn.click({force: true}); + await this.click(this.currentUserAvatarBtn, {force: true}); } async clickCreateActionButton() { - await expect(this.createActionBtn).toBeVisible(); - await this.createActionBtn.click(); + await this.click(this.createActionBtn); } async clickCreateActionWithOptionName(optionName: string) { await this.clickCreateActionButton(); const createOptionLocator = this.createActionButtonCollection.locator('[label="' + optionName + '"]'); - await expect(createOptionLocator).toBeVisible(); - await createOptionLocator.click(); + await this.click(createOptionLocator); } async doesCollectionTreeItemTableRowHaveName(name: string) { - await expect(this.collectionTreeItemTableRow.first()).toBeVisible(); - await expect(this.collectionTreeItemTableRow.locator('[label="' + name + '"]')).toBeVisible(); + await this.waitForVisible(this.collectionTreeItemTableRow.first()); + await this.isVisible(this.collectionTreeItemTableRow.locator('[label="' + name + '"]')); } async doesCollectionTreeItemTableRowHaveIcon(name: string, icon: string) { - await expect(this.collectionTreeItemTableRow.first()).toBeVisible(); - await expect(this.collectionTreeItemTableRow.filter({hasText: name}).locator('umb-icon').locator('[name="' + icon + '"]')).toBeVisible(); + await this.waitForVisible(this.collectionTreeItemTableRow.first()); + await this.isVisible(this.collectionTreeItemTableRow.filter({hasText: name}).locator('umb-icon').locator('[name="' + icon + '"]')); } async clickFolderButton() { - await expect(this.folderBtn).toBeVisible(); - await this.folderBtn.click(); + await this.click(this.folderBtn); } async doesReferenceHeadlineHaveText(text: string) { - await expect(this.referenceHeadline).toContainText(text); + await this.containsText(this.referenceHeadline, text); } async isReferenceHeadlineVisible(isVisible: boolean) { - await expect(this.referenceHeadline).toBeVisible({visible: isVisible}); + await this.isVisible(this.referenceHeadline, isVisible); } async doesReferenceItemsHaveCount(count: number) { - await expect(this.entityItemRef).toHaveCount(count); + await this.hasCount(this.entityItemRef, count); } async isReferenceItemNameVisible(itemName: string) { - await expect(this.entityItemRef.locator('uui-ref-node[name="' + itemName + '"]')).toBeVisible(); + await this.isVisible(this.entityItemRef.locator('uui-ref-node[name="' + itemName + '"]')); } async doesReferencesContainText(text: string) { - await expect(this.confirmActionModalEntityReferences).toContainText(text); + await this.containsText(this.confirmActionModalEntityReferences, text); } async isValidationMessageVisible(message: string, isVisible: boolean = true) { - await expect(this.validationMessage.filter({hasText: message})).toBeVisible({visible: isVisible}); + await this.isVisible(this.validationMessage.filter({hasText: message}), isVisible); } async isSuccessStateIconVisible() { - await expect(this.successStateIcon).toBeVisible(); + await this.isVisible(this.successStateIcon); } async isPropertyEditorUiWithNameReadOnly(name: string) { const propertyEditorUiLocator = this.page.locator('umb-property-editor-ui-' + name); - await expect(propertyEditorUiLocator).toHaveAttribute('readonly'); + await this.hasAttribute(propertyEditorUiLocator, 'readonly', ''); } async isPropertyEditorUiWithNameVisible(name: string, isVisible: boolean = true) { const propertyEditorUiLocator = this.page.locator('umb-property-editor-ui-' + name); - await expect(propertyEditorUiLocator).toBeVisible({visible: isVisible}); + await this.isVisible(propertyEditorUiLocator, isVisible); } // Entity Action @@ -1420,38 +1299,35 @@ export class UiBaseLocators extends BasePage { } async clickModalMenuItemWithName(name: string) { - await expect(this.openedModal.locator('uui-menu-item[label="' + name + '"]')).toBeVisible(); - await this.openedModal.locator('uui-menu-item[label="' + name + '"]').click(); + await this.click(this.openedModal.locator('uui-menu-item[label="' + name + '"]')); } async isModalMenuItemWithNameDisabled(name: string) { - await expect(this.sidebarModal.locator('uui-menu-item[label="' + name + '"]')).toHaveAttribute('disabled'); + await this.hasAttribute(this.sidebarModal.locator('uui-menu-item[label="' + name + '"]'), 'disabled', ''); } async doesPropertyHaveInvalidBadge(propertyName: string) { - await expect(this.page.locator('umb-property-layout').filter({hasText: propertyName}).locator('#invalid-badge uui-badge')).toBeVisible(); + await this.isVisible(this.page.locator('umb-property-layout').filter({hasText: propertyName}).locator('#invalid-badge uui-badge')); } async isModalMenuItemWithNameVisible(name: string, isVisible: boolean = true) { - await expect(this.sidebarModal.locator('uui-menu-item[label="' + name + '"]')).toBeVisible({visible: isVisible}); + await this.isVisible(this.sidebarModal.locator('uui-menu-item[label="' + name + '"]'), isVisible); } async clickEntityItemByName(itemName: string) { - await expect(this.page.locator('uui-ref-node,umb-ref-item[name="' + itemName + '"]')).toBeVisible(); - await this.page.locator('uui-ref-node,umb-ref-item[name="' + itemName + '"]').click(); + await this.click(this.page.locator('uui-ref-node,umb-ref-item[name="' + itemName + '"]')); } async isMediaCardItemWithNameDisabled(itemName: string) { - await expect(this.mediaCardItems.filter({hasText: itemName})).toHaveAttribute('class', 'not-allowed'); + await this.hasAttribute(this.mediaCardItems.filter({hasText: itemName}), 'class', 'not-allowed'); } async isMediaCardItemWithNameVisible(itemName: string, isVisible: boolean = true) { - await expect(this.mediaCardItems.filter({hasText: itemName})).toBeVisible({visible: isVisible}); + await this.isVisible(this.mediaCardItems.filter({hasText: itemName}), isVisible); } async clickWorkspaceActionMenuButton() { - await expect(this.workspaceActionMenuBtn).toBeVisible(); - await this.workspaceActionMenuBtn.click(); + await this.click(this.workspaceActionMenuBtn); } async clickLockActionMenuOption() { @@ -1459,45 +1335,43 @@ export class UiBaseLocators extends BasePage { } async isDashboardTabWithNameVisible(name: string, isVisible: boolean = true) { - await expect(this.page.locator('uui-tab[label="' + name + '"]')).toBeVisible({visible: isVisible}); + await this.isVisible(this.page.locator('uui-tab[label="' + name + '"]'), isVisible); } async enterMonacoEditorValue(value: string) { - await expect(this.monacoEditor).toBeVisible(); - await this.monacoEditor.click(); + await this.click(this.monacoEditor); await this.page.keyboard.press('Control+A'); await this.page.keyboard.press('Backspace'); await this.page.keyboard.insertText(value); } async waitUntilUiLoaderIsNoLongerVisible() { - await expect(this.uiLoader).toBeVisible({visible: false, timeout: 10000}); + await this.waitForHidden(this.uiLoader, 10000); } async isWorkspaceViewTabWithAliasVisible(alias: string, isVisible: boolean = true) { - await expect(this.page.getByTestId('workspace:view-link:' + alias)).toBeVisible({visible: isVisible}); + await this.isVisible(this.page.getByTestId('workspace:view-link:' + alias), isVisible); } async clickRestoreButton() { - await expect(this.restoreBtn).toBeVisible(); - await this.restoreBtn.click(); + await this.click(this.restoreBtn); } async isInputDropzoneVisible(isVisible: boolean = true) { - await expect(this.inputDropzone).toBeVisible({visible: isVisible}); + await this.isVisible(this.inputDropzone, isVisible); } async isImageCropperFieldVisible(isVisible: boolean = true) { - await expect(this.imageCropperField).toBeVisible({visible: isVisible}); + await this.isVisible(this.imageCropperField, isVisible); } async isInputUploadFieldVisible(isVisible: boolean = true) { - await expect(this.inputUploadField).toBeVisible({visible: isVisible}); + await this.isVisible(this.inputUploadField, isVisible); } async isBackOfficeMainVisible(isVisible: boolean = true) { // We need to wait to make sure the page has loaded - await this.page.waitForTimeout(1000); - await expect(this.backOfficeMain).toBeVisible({visible: isVisible}); + await this.page.waitForTimeout(ConstantHelper.wait.medium); + await this.isVisible(this.backOfficeMain, isVisible); } } diff --git a/lib/helpers/index.ts b/lib/helpers/index.ts index f4938c10..5c676026 100644 --- a/lib/helpers/index.ts +++ b/lib/helpers/index.ts @@ -3,4 +3,5 @@ export {UiHelpers} from './UiHelpers'; export {AliasHelper} from './AliasHelper'; export {test} from './testExtension'; export {ConstantHelper} from './ConstantHelper'; -export {NotificationConstantHelper} from './NotificationConstantHelper'; \ No newline at end of file +export {NotificationConstantHelper} from './NotificationConstantHelper'; +export {BasePage} from './BasePage'; \ No newline at end of file From eadb4bd06df843929331e139c37e758d216931c4 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 13:40:29 +0700 Subject: [PATCH 04/34] Add .claude folder to gitignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index eafb7dbb..f3ddee4c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ node_modules/ test-results/ playwright-report/ .idea/ +.claude/ /tsconfig.tsbuildinfo /dist/ From 8fac92e2fe690ed11676fbe532873f3774844b5c Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 13:51:44 +0700 Subject: [PATCH 05/34] Restructure UiBaseLocators.ts with region comments for better organization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Organize 160+ locators into 25 logical region groups - Organize 150+ methods into corresponding region groups - Group related functionality: Core Actions, Folders, Navigation, Modals, Document Types, Properties, Validation, Media, Block Elements, etc. - Improve code navigation and maintainability 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- lib/helpers/UiBaseLocators.ts | 1650 +++++++++++++++++++-------------- 1 file changed, 950 insertions(+), 700 deletions(-) diff --git a/lib/helpers/UiBaseLocators.ts b/lib/helpers/UiBaseLocators.ts index 36c36344..c6697ec9 100644 --- a/lib/helpers/UiBaseLocators.ts +++ b/lib/helpers/UiBaseLocators.ts @@ -1,336 +1,519 @@ -import {expect, Locator, Page} from "@playwright/test" +import {expect, Locator, Page} from "@playwright/test" import {ConstantHelper} from "./ConstantHelper"; import {BasePage} from "./BasePage"; export class UiBaseLocators extends BasePage { + // #region Core Action Buttons public readonly saveBtn: Locator; - public readonly chooseBtn: Locator; public readonly submitBtn: Locator; - public readonly createFolderBtn: Locator; - public readonly breadcrumbBtn: Locator; - public readonly confirmToDeleteBtn: Locator; + public readonly confirmBtn: Locator; + public readonly chooseBtn: Locator; + public readonly chooseModalBtn: Locator; + public readonly createBtn: Locator; + public readonly addBtn: Locator; + public readonly updateBtn: Locator; + public readonly changeBtn: Locator; + public readonly deleteBtn: Locator; public readonly deleteExactBtn: Locator; - public readonly confirmCreateFolderBtn: Locator; + public readonly removeExactBtn: Locator; public readonly insertBtn: Locator; - public readonly modalCaretBtn: Locator; - public readonly queryBuilderBtn: Locator; - public readonly queryBuilderOrderedBy: Locator; - public readonly queryBuilderCreateDate: Locator; + public readonly renameBtn: Locator; + public readonly reloadBtn: Locator; + public readonly reloadChildrenBtn: Locator; + public readonly restoreBtn: Locator; + public readonly disableBtn: Locator; + public readonly enableBtn: Locator; + public readonly actionBtn: Locator; + public readonly nextBtn: Locator; + // #endregion + + // #region Confirmation Buttons + public readonly confirmToDeleteBtn: Locator; + public readonly confirmCreateFolderBtn: Locator; + public readonly confirmToRemoveBtn: Locator; + public readonly confirmToSubmitBtn: Locator; + public readonly confirmDisableBtn: Locator; + public readonly confirmEnableBtn: Locator; + public readonly confirmRenameBtn: Locator; + public readonly confirmTrashBtn: Locator; + // #endregion + + // #region Folder Management + public readonly createFolderBtn: Locator; public readonly folderNameTxt: Locator; - public readonly textAreaInputArea: Locator; - public readonly wherePropertyAliasBtn: Locator; - public readonly whereOperatorBtn: Locator; - public readonly whereConstrainValueTxt: Locator; - public readonly orderByPropertyAliasBtn: Locator; - public readonly ascendingBtn: Locator; - public readonly queryBuilderShowCode: Locator; - public readonly createThreeDotsBtn: Locator; + public readonly folderBtn: Locator; public readonly newFolderThreeDotsBtn: Locator; - public readonly renameThreeDotsBtn: Locator; - public readonly newNameTxt: Locator; - public readonly renameModalBtn: Locator; - public readonly createBtn: Locator; - public readonly successState: Locator; - public readonly chooseModalBtn: Locator; - public readonly addBtn: Locator; public readonly renameFolderThreeDotsBtn: Locator; public readonly renameFolderBtn: Locator; public readonly updateFolderBtn: Locator; - public readonly filterChooseBtn: Locator; - public readonly updateBtn: Locator; - public readonly changeBtn: Locator; + public readonly deleteFolderThreeDotsBtn: Locator; + // #endregion + + // #region Navigation & Menu + public readonly breadcrumbBtn: Locator; + public readonly leftArrowBtn: Locator; + public readonly caretBtn: Locator; + public readonly modalCaretBtn: Locator; + public readonly backOfficeHeader: Locator; + public readonly backOfficeMain: Locator; + public readonly sectionLinks: Locator; + public readonly sectionSidebar: Locator; + public readonly menuItem: Locator; + public readonly actionsMenuContainer: Locator; + // #endregion + + // #region Three Dots Menu Buttons + public readonly createThreeDotsBtn: Locator; + public readonly renameThreeDotsBtn: Locator; + public readonly deleteThreeDotsBtn: Locator; + // #endregion + + // #region Modal & Container + public readonly sidebarModal: Locator; + public readonly openedModal: Locator; + public readonly container: Locator; + public readonly containerChooseBtn: Locator; + public readonly containerSaveAndPublishBtn: Locator; + public readonly createModalBtn: Locator; + // #endregion + + // #region Document Type & Property Editor + public readonly documentTypeNode: Locator; public readonly propertyNameTxt: Locator; public readonly selectPropertyEditorBtn: Locator; + public readonly editorSettingsBtn: Locator; + public readonly enterPropertyEditorDescriptionTxt: Locator; + public readonly property: Locator; + public readonly addPropertyBtn: Locator; + public readonly labelAboveBtn: Locator; + // #endregion + + // #region Group & Tab Management public readonly addGroupBtn: Locator; - public readonly iAmDoneReorderingBtn: Locator; - public readonly reorderBtn: Locator; - public readonly compositionsBtn: Locator; + public readonly groupLabel: Locator; + public readonly typeGroups: Locator; public readonly addTabBtn: Locator; - public readonly descriptionBtn: Locator; - public readonly enterDescriptionTxt: Locator; + public readonly unnamedTabTxt: Locator; + public readonly structureTabBtn: Locator; + // #endregion + + // #region Validation & Mandatory public readonly mandatoryToggle: Locator; public readonly validation: Locator; public readonly regexTxt: Locator; public readonly regexMessageTxt: Locator; - public readonly structureTabBtn: Locator; + public readonly validationMessage: Locator; + // #endregion + + // #region Composition & Structure + public readonly compositionsBtn: Locator; public readonly allowAtRootBtn: Locator; - public readonly addPropertyBtn: Locator; - public readonly typeToFilterSearchTxt: Locator; - public readonly editorSettingsBtn: Locator; - public readonly labelAboveBtn: Locator; - public readonly unnamedTabTxt: Locator; - public readonly deleteThreeDotsBtn: Locator; - public readonly removeExactBtn: Locator; - public readonly confirmBtn: Locator; - public readonly disableBtn: Locator; - public readonly confirmDisableBtn: Locator; - public readonly enableBtn: Locator; - public readonly confirmEnableBtn: Locator; - public readonly iconBtn: Locator; - public readonly aliasLockBtn: Locator; - public readonly aliasNameTxt: Locator; - public readonly deleteFolderThreeDotsBtn: Locator; - public readonly createLink: Locator; + public readonly allowedChildNodesModal: Locator; + public readonly addCollectionBtn: Locator; + // #endregion + + // #region Reorder + public readonly iAmDoneReorderingBtn: Locator; + public readonly reorderBtn: Locator; + // #endregion + + // #region Query Builder + public readonly queryBuilderBtn: Locator; + public readonly queryBuilderOrderedBy: Locator; + public readonly queryBuilderCreateDate: Locator; + public readonly queryBuilderShowCode: Locator; + public readonly wherePropertyAliasBtn: Locator; + public readonly whereOperatorBtn: Locator; + public readonly whereConstrainValueTxt: Locator; + public readonly orderByPropertyAliasBtn: Locator; + public readonly ascendingBtn: Locator; + public readonly chooseRootContentBtn: Locator; + public readonly returnedItemsCount: Locator; + public readonly queryResults: Locator; + // #endregion + + // #region Insert & Template public readonly insertValueBtn: Locator; public readonly insertPartialViewBtn: Locator; public readonly insertDictionaryItemBtn: Locator; public readonly chooseFieldDropDown: Locator; public readonly systemFieldsOption: Locator; public readonly chooseFieldValueDropDown: Locator; - public readonly renameBtn: Locator; - public readonly returnedItemsCount: Locator; - public readonly chooseRootContentBtn: Locator; - public readonly queryResults: Locator; - public readonly reloadBtn: Locator; - public readonly confirmToRemoveBtn: Locator; - public readonly confirmToSubmitBtn: Locator; - public readonly typeGroups: Locator; - public readonly allowedChildNodesModal: Locator; - public readonly addCollectionBtn: Locator; - public readonly errorNotification: Locator; - public readonly confirmRenameBtn: Locator; - public readonly successNotification: Locator; - public readonly leftArrowBtn: Locator; - public readonly clickToUploadBtn: Locator; - public readonly backOfficeHeader: Locator; + public readonly breadcrumbsTemplateModal: Locator; + // #endregion + + // #region Rename + public readonly newNameTxt: Locator; + public readonly renameModalBtn: Locator; + // #endregion + + // #region State & Notification + public readonly successState: Locator; + public readonly successStateIcon: Locator; public readonly failedStateButton: Locator; - public readonly sidebarModal: Locator; + public readonly successNotification: Locator; + public readonly errorNotification: Locator; + // #endregion + + // #region Search & Filter + public readonly typeToFilterSearchTxt: Locator; + public readonly filterChooseBtn: Locator; + // #endregion + + // #region Text Input + public readonly textAreaInputArea: Locator; public readonly enterAName: Locator; - public readonly mediaCardItems: Locator; - public readonly enterPropertyEditorDescriptionTxt: Locator; - public readonly breadcrumbsTemplateModal: Locator; - public readonly containerChooseBtn: Locator; - public readonly documentTypeNode: Locator; - public readonly groupLabel: Locator; - public readonly containerSaveAndPublishBtn: Locator; - public readonly confirmTrashBtn: Locator; + public readonly descriptionBtn: Locator; + public readonly enterDescriptionTxt: Locator; + public readonly aliasLockBtn: Locator; + public readonly aliasNameTxt: Locator; + // #endregion + + // #region Icon + public readonly iconBtn: Locator; + // #endregion + + // #region Create Link + public readonly createLink: Locator; + // #endregion + + // #region Recycle Bin public readonly recycleBinBtn: Locator; - public readonly recycleBinMenuItemCaretBtn: Locator; public readonly recycleBinMenuItem: Locator; + public readonly recycleBinMenuItemCaretBtn: Locator; + // #endregion + + // #region View Options public readonly gridBtn: Locator; public readonly listBtn: Locator; public readonly viewBundleBtn: Locator; - public readonly chooseDocumentInputBtn: Locator; - public readonly chooseMediaInputBtn: Locator; - public readonly container: Locator; - public readonly createDocumentBlueprintBtn: Locator; - public readonly actionBtn: Locator; + // #endregion + + // #region Media + public readonly mediaCardItems: Locator; public readonly mediaPickerModalSubmitBtn: Locator; - public readonly deleteBtn: Locator; - public readonly createModalBtn: Locator; public readonly mediaCaptionAltTextModalSubmitBtn: Locator; + public readonly clickToUploadBtn: Locator; + public readonly inputDropzone: Locator; + public readonly imageCropperField: Locator; + public readonly inputUploadField: Locator; + public readonly chooseMediaInputBtn: Locator; + // #endregion + + // #region Embedded Media public readonly embeddedMediaModal: Locator; public readonly embeddedURLTxt: Locator; public readonly embeddedRetrieveBtn: Locator; public readonly embeddedMediaModalConfirmBtn: Locator; public readonly embeddedPreview: Locator; - public readonly sectionSidebar: Locator; - public readonly actionsMenuContainer: Locator; - public readonly menuItem: Locator; - public readonly property: Locator; + // #endregion + + // #region Document & Content + public readonly chooseDocumentInputBtn: Locator; + public readonly createDocumentBlueprintBtn: Locator; + public readonly createDocumentBlueprintModal: Locator; + public readonly createNewDocumentBlueprintBtn: Locator; + // #endregion + + // #region User public readonly currentUserAvatarBtn: Locator; public readonly newPasswordTxt: Locator; public readonly confirmPasswordTxt: Locator; public readonly currentPasswordTxt: Locator; - public readonly createOptionActionListModal: Locator; + // #endregion + + // #region Collection & Table + public readonly collectionTreeItemTableRow: Locator; public readonly createActionButtonCollection: Locator; public readonly createActionBtn: Locator; - public readonly collectionTreeItemTableRow: Locator; - public readonly folderBtn: Locator; - public readonly reloadChildrenBtn: Locator; + public readonly createOptionActionListModal: Locator; + // #endregion + + // #region Reference & Entity public readonly confirmActionModalEntityReferences: Locator; public readonly referenceHeadline: Locator; public readonly entityItemRef: Locator; - public readonly validationMessage: Locator; - public readonly successStateIcon: Locator; + public readonly entityItem: Locator; + // #endregion + + // #region Workspace & Action public readonly workspaceAction: Locator; + public readonly workspaceActionMenuBtn: Locator; public readonly entityAction: Locator; public readonly openEntityAction: Locator; - public readonly caretBtn: Locator; - public readonly workspaceActionMenuBtn: Locator; - public readonly monacoEditor: Locator; - public readonly createNewDocumentBlueprintBtn: Locator; - public readonly openedModal: Locator; - public readonly uiLoader: Locator; - public readonly createDocumentBlueprintModal: Locator; - public readonly inputDropzone: Locator; - public readonly imageCropperField: Locator; - public readonly inputUploadField: Locator; - public readonly entityItem: Locator; - public readonly sectionLinks: Locator; - public readonly restoreBtn: Locator; - public readonly backOfficeMain: Locator; + // #endregion + + // #region Pagination public readonly firstPaginationBtn: Locator; public readonly nextPaginationBtn: Locator; - public readonly nextBtn: Locator; + // #endregion + + // #region Editor + public readonly monacoEditor: Locator; + // #endregion + + // #region Loader + public readonly uiLoader: Locator; + // #endregion constructor(page: Page) { super(page); + + // #region Core Action Buttons this.saveBtn = page.getByLabel('Save', {exact: true}); this.submitBtn = page.getByLabel('Submit'); + this.confirmBtn = page.getByLabel('Confirm'); + this.chooseBtn = page.getByLabel('Choose', {exact: true}); + this.chooseModalBtn = page.locator('uui-modal-sidebar').locator('[look="primary"]').getByLabel('Choose'); + this.createBtn = page.getByRole('button', {name: /^Create(…)?$/}); + this.addBtn = page.getByRole('button', {name: 'Add', exact: true}); + this.updateBtn = page.getByLabel('Update'); + this.changeBtn = page.getByLabel('Change'); + this.deleteBtn = page.getByRole('button', {name: /^Delete(…)?$/}); this.deleteExactBtn = page.getByRole('button', {name: 'Delete', exact: true}); + this.removeExactBtn = page.getByLabel('Remove', {exact: true}); + this.insertBtn = page.locator('uui-box uui-button').filter({hasText: 'Insert'}); + this.renameBtn = page.getByRole('button', {name: /^Rename(…)?$/}); + this.reloadBtn = page.getByRole('button', {name: 'Reload', exact: true}); + this.reloadChildrenBtn = page.getByRole('button', {name: 'Reload children'}); + this.restoreBtn = page.getByLabel('Restore', {exact: true}); + this.disableBtn = page.getByLabel('Disable', {exact: true}); + this.enableBtn = page.getByLabel('Enable'); + this.actionBtn = page.getByTestId('workspace:action-menu-button'); + this.nextBtn = page.getByLabel('Next'); + // #endregion + + // #region Confirmation Buttons this.confirmToDeleteBtn = page.locator('#confirm').getByLabel('Delete'); this.confirmCreateFolderBtn = page.locator('#confirm').getByLabel('Create Folder'); - this.breadcrumbBtn = page.getByLabel('Breadcrumb'); + this.confirmToRemoveBtn = page.locator('#confirm').getByLabel('Remove'); + this.confirmToSubmitBtn = page.locator('#confirm').getByLabel('Submit'); + this.confirmDisableBtn = page.locator('#confirm').getByLabel('Disable'); + this.confirmEnableBtn = page.locator('#confirm').getByLabel('Enable'); + this.confirmRenameBtn = page.locator('#confirm').getByLabel('Rename'); + this.confirmTrashBtn = page.locator('#confirm').getByLabel('Trash'); + // #endregion + + // #region Folder Management this.createFolderBtn = page.getByLabel('Create folder'); - this.insertBtn = page.locator('uui-box uui-button').filter({hasText: 'Insert'}); - this.sidebarModal = page.locator('uui-modal-sidebar'); - this.modalCaretBtn = this.sidebarModal.locator('#caret-button'); - this.enterAName = page.getByLabel('Enter a name...', {exact: true}); - this.queryBuilderBtn = page.locator('#query-builder-button'); - this.queryBuilderOrderedBy = page.locator('#property-alias-dropdown').getByLabel('Property alias'); - this.queryBuilderCreateDate = page.locator('#property-alias-dropdown').getByText('CreateDate').locator(".."); this.folderNameTxt = page.getByLabel('Enter a folder name'); - this.textAreaInputArea = page.locator('textarea.ime-text-area'); - this.wherePropertyAliasBtn = page.locator('#property-alias-dropdown'); - this.whereOperatorBtn = page.locator('#operator-dropdown'); - this.whereConstrainValueTxt = page.getByLabel('constrain value'); - this.orderByPropertyAliasBtn = page.locator('#sort-dropdown'); - this.ascendingBtn = page.locator('[key="template_ascending"]'); - this.queryBuilderShowCode = page.locator('umb-code-block'); - this.createThreeDotsBtn = page.getByText('Create…', {exact: true}); - this.chooseBtn = page.getByLabel('Choose', {exact: true}); - this.containerChooseBtn = page.locator('#container').getByLabel('Choose'); - this.containerSaveAndPublishBtn = page.locator('#container').getByLabel('Save and Publish'); + this.folderBtn = page.locator('umb-entity-create-option-action-list-modal').locator('umb-ref-item', {hasText: 'Folder'}); this.newFolderThreeDotsBtn = page.getByLabel('New Folder…'); - this.renameThreeDotsBtn = page.getByLabel('Rename…', {exact: true}); - this.newNameTxt = page.getByRole('textbox', {name: 'Enter new name...'}); - this.renameModalBtn = page.locator('umb-rename-modal').getByLabel('Rename'); - this.createBtn = page.getByRole('button', {name: /^Create(…)?$/}); - this.actionsMenuContainer = page.locator('uui-scroll-container'); - this.successState = page.locator('[state="success"]'); - this.chooseModalBtn = this.sidebarModal.locator('[look="primary"]').getByLabel('Choose'); - this.addBtn = page.getByRole('button', {name: 'Add', exact: true}); - this.renameFolderThreeDotsBtn = page.getByRole('button', {name: 'Rename folder…'}) + this.renameFolderThreeDotsBtn = page.getByRole('button', {name: 'Rename folder…'}); this.renameFolderBtn = page.getByLabel('Rename folder'); - this.confirmRenameBtn = page.locator('#confirm').getByLabel('Rename'); this.updateFolderBtn = page.getByLabel('Update folder'); - this.filterChooseBtn = page.locator('button').filter({hasText: 'Choose'}); - this.updateBtn = page.getByLabel('Update'); - this.changeBtn = page.getByLabel('Change'); + this.deleteFolderThreeDotsBtn = page.locator('#action-modal').getByLabel('Delete Folder...'); + // #endregion + + // #region Navigation & Menu + this.breadcrumbBtn = page.getByLabel('Breadcrumb'); + this.leftArrowBtn = page.locator('[name="icon-arrow-left"] svg'); + this.caretBtn = page.locator('#caret-button'); + this.modalCaretBtn = page.locator('uui-modal-sidebar').locator('#caret-button'); + this.backOfficeHeader = page.locator('umb-backoffice-header'); + this.backOfficeMain = page.locator('umb-backoffice-main'); + this.sectionLinks = page.getByTestId('section-links'); + this.sectionSidebar = page.locator('umb-section-sidebar'); + this.menuItem = page.locator('uui-menu-item'); + this.actionsMenuContainer = page.locator('uui-scroll-container'); + // #endregion + + // #region Three Dots Menu Buttons + this.createThreeDotsBtn = page.getByText('Create…', {exact: true}); + this.renameThreeDotsBtn = page.getByLabel('Rename…', {exact: true}); + this.deleteThreeDotsBtn = page.getByLabel('Delete…'); + // #endregion + + // #region Modal & Container + this.sidebarModal = page.locator('uui-modal-sidebar'); + this.openedModal = page.locator('uui-modal-container[backdrop]'); + this.container = page.locator('#container'); + this.containerChooseBtn = page.locator('#container').getByLabel('Choose'); + this.containerSaveAndPublishBtn = page.locator('#container').getByLabel('Save and Publish'); + this.createModalBtn = page.locator('uui-modal-sidebar').getByLabel('Create', {exact: true}); + // #endregion + + // #region Document Type & Property Editor + this.documentTypeNode = page.locator('uui-ref-node-document-type'); this.propertyNameTxt = page.getByTestId('input:entity-name').locator('#input').first(); this.selectPropertyEditorBtn = page.getByLabel('Select Property Editor'); + this.editorSettingsBtn = page.getByLabel('Editor settings'); + this.enterPropertyEditorDescriptionTxt = page.locator('uui-modal-sidebar').getByTestId('input:entity-description').locator('#textarea'); + this.property = page.locator('umb-property'); + this.addPropertyBtn = page.getByLabel('Add property', {exact: true}); + this.labelAboveBtn = page.locator('.appearance-option').filter({hasText: 'Label above'}); + // #endregion + + // #region Group & Tab Management this.addGroupBtn = page.getByLabel('Add group', {exact: true}); - this.iAmDoneReorderingBtn = page.getByLabel('I am done reordering'); - this.reorderBtn = page.getByLabel('Reorder'); - this.compositionsBtn = page.getByLabel('Compositions'); - this.addTabBtn = page.getByLabel('Add tab'); - this.descriptionBtn = page.getByLabel('Description'); - this.enterDescriptionTxt = page.getByLabel('Enter a description...'); + this.groupLabel = page.getByLabel('Group', {exact: true}); + this.typeGroups = page.locator('umb-content-type-design-editor-group'); + this.addTabBtn = page.getByLabel('Add tab'); + this.unnamedTabTxt = page.getByTestId('tab:').getByTestId('tab:name-input').locator('#input'); + this.structureTabBtn = page.locator('uui-tab').filter({hasText: 'Structure'}).locator('svg'); + // #endregion + + // #region Validation & Mandatory this.mandatoryToggle = page.locator('#mandatory #toggle'); this.validation = page.locator('#native'); this.regexTxt = page.locator('input[name="pattern"]'); this.regexMessageTxt = page.locator('textarea[name="pattern-message"]'); - this.structureTabBtn = page.locator('uui-tab').filter({hasText: 'Structure'}).locator('svg'); + this.validationMessage = page.locator('umb-form-validation-message').locator('#messages'); + // #endregion + + // #region Composition & Structure + this.compositionsBtn = page.getByLabel('Compositions'); this.allowAtRootBtn = page.locator('label').filter({hasText: 'Allow at root'}); - this.addPropertyBtn = page.getByLabel('Add property', {exact: true}); - this.typeToFilterSearchTxt = page.locator('[type="search"] #input'); - this.editorSettingsBtn = page.getByLabel('Editor settings'); - this.labelAboveBtn = page.locator('.appearance-option').filter({hasText: 'Label above'}); - this.firstPaginationBtn = this.page.locator('umb-collection-pagination').getByLabel('First'); - this.nextPaginationBtn = this.page.locator('umb-collection-pagination').getByLabel('Next'); - this.nextBtn = this.page.getByLabel('Next'); - // tab: means that the tab is unnamed - this.unnamedTabTxt = page.getByTestId('tab:').getByTestId('tab:name-input').locator('#input'); - this.deleteThreeDotsBtn = page.getByLabel('Delete…'); - this.removeExactBtn = page.getByLabel('Remove', {exact: true}); - this.confirmBtn = page.getByLabel('Confirm'); - this.disableBtn = page.getByLabel('Disable', {exact: true}); - this.confirmDisableBtn = page.locator('#confirm').getByLabel('Disable'); - this.confirmToSubmitBtn = page.locator('#confirm').getByLabel('Submit'); - this.enableBtn = page.getByLabel('Enable'); - this.confirmEnableBtn = page.locator('#confirm').getByLabel('Enable'); - this.iconBtn = page.getByLabel('icon'); - this.aliasLockBtn = page.locator('#name').getByLabel('Unlock input'); - this.aliasNameTxt = page.locator('#name').getByLabel('alias'); - this.deleteFolderThreeDotsBtn = page.locator('#action-modal').getByLabel('Delete Folder...'); - this.createLink = page.getByRole('link', {name: 'Create', exact: true}); + this.allowedChildNodesModal = page.locator('umb-tree-picker-modal'); + this.addCollectionBtn = page.locator('umb-input-content-type-collection-configuration #create-button'); + // #endregion + + // #region Reorder + this.iAmDoneReorderingBtn = page.getByLabel('I am done reordering'); + this.reorderBtn = page.getByLabel('Reorder'); + // #endregion + + // #region Query Builder + this.queryBuilderBtn = page.locator('#query-builder-button'); + this.queryBuilderOrderedBy = page.locator('#property-alias-dropdown').getByLabel('Property alias'); + this.queryBuilderCreateDate = page.locator('#property-alias-dropdown').getByText('CreateDate').locator(".."); + this.queryBuilderShowCode = page.locator('umb-code-block'); + this.wherePropertyAliasBtn = page.locator('#property-alias-dropdown'); + this.whereOperatorBtn = page.locator('#operator-dropdown'); + this.whereConstrainValueTxt = page.getByLabel('constrain value'); + this.orderByPropertyAliasBtn = page.locator('#sort-dropdown'); + this.ascendingBtn = page.locator('[key="template_ascending"]'); + this.chooseRootContentBtn = page.getByLabel('Choose root document'); + this.returnedItemsCount = page.locator('#results-count'); + this.queryResults = page.locator('.query-results'); + // #endregion + + // #region Insert & Template this.insertValueBtn = page.locator('uui-button').filter({has: page.locator('[key="template_insertPageField"]')}); this.insertPartialViewBtn = page.locator('uui-button').filter({has: page.locator('[key="template_insertPartialView"]')}); this.insertDictionaryItemBtn = page.locator('uui-button').filter({has: page.locator('[key="template_insertDictionaryItem"]')}); this.chooseFieldDropDown = page.locator('#preview #expand-symbol-wrapper'); this.systemFieldsOption = page.getByText('System fields'); this.chooseFieldValueDropDown = page.locator('#value #expand-symbol-wrapper'); - this.renameBtn = page.getByRole('button', {name: /^Rename(…)?$/}); - this.returnedItemsCount = page.locator('#results-count'); - this.chooseRootContentBtn = page.getByLabel('Choose root document'); - this.queryResults = page.locator('.query-results'); - this.reloadBtn = page.getByRole('button', {name: 'Reload', exact: true}); - this.confirmToRemoveBtn = page.locator('#confirm').getByLabel('Remove'); - this.typeGroups = page.locator('umb-content-type-design-editor-group'); - this.allowedChildNodesModal = page.locator('umb-tree-picker-modal'); - this.addCollectionBtn = page.locator('umb-input-content-type-collection-configuration #create-button'); - this.errorNotification = page.locator('uui-toast-notification[open][color="danger"]'); - this.successNotification = page.locator('uui-toast-notification[open][color="positive"]'); - this.leftArrowBtn = page.locator('[name="icon-arrow-left"] svg'); - this.clickToUploadBtn = page.locator('#splitViews').getByRole('button', {name: 'Click to upload'}); - this.backOfficeHeader = page.locator('umb-backoffice-header'); + this.breadcrumbsTemplateModal = page.locator('uui-modal-sidebar').locator('umb-template-workspace-editor uui-breadcrumbs'); + // #endregion + + // #region Rename + this.newNameTxt = page.getByRole('textbox', {name: 'Enter new name...'}); + this.renameModalBtn = page.locator('umb-rename-modal').getByLabel('Rename'); + // #endregion + + // #region State & Notification + this.successState = page.locator('[state="success"]'); + this.successStateIcon = page.locator('[state="success"]').locator('#state'); this.failedStateButton = page.locator('uui-button[state="failed"]'); - this.mediaCardItems = page.locator('uui-card-media'); - this.enterPropertyEditorDescriptionTxt = this.sidebarModal.getByTestId('input:entity-description').locator('#textarea'); - this.breadcrumbsTemplateModal = this.sidebarModal.locator('umb-template-workspace-editor uui-breadcrumbs'); - this.documentTypeNode = page.locator('uui-ref-node-document-type'); - this.groupLabel = page.getByLabel('Group', {exact: true}); - this.confirmTrashBtn = page.locator('#confirm').getByLabel('Trash'); + this.successNotification = page.locator('uui-toast-notification[open][color="positive"]'); + this.errorNotification = page.locator('uui-toast-notification[open][color="danger"]'); + // #endregion + + // #region Search & Filter + this.typeToFilterSearchTxt = page.locator('[type="search"] #input'); + this.filterChooseBtn = page.locator('button').filter({hasText: 'Choose'}); + // #endregion + + // #region Text Input + this.textAreaInputArea = page.locator('textarea.ime-text-area'); + this.enterAName = page.getByLabel('Enter a name...', {exact: true}); + this.descriptionBtn = page.getByLabel('Description'); + this.enterDescriptionTxt = page.getByLabel('Enter a description...'); + this.aliasLockBtn = page.locator('#name').getByLabel('Unlock input'); + this.aliasNameTxt = page.locator('#name').getByLabel('alias'); + // #endregion + + // #region Icon + this.iconBtn = page.getByLabel('icon'); + // #endregion + + // #region Create Link + this.createLink = page.getByRole('link', {name: 'Create', exact: true}); + // #endregion + + // #region Recycle Bin this.recycleBinBtn = page.getByLabel('Recycle Bin', {exact: true}); this.recycleBinMenuItem = page.locator('uui-menu-item[label="Recycle Bin"]'); - this.recycleBinMenuItemCaretBtn = this.recycleBinMenuItem.locator('#caret-button'); + this.recycleBinMenuItemCaretBtn = page.locator('uui-menu-item[label="Recycle Bin"]').locator('#caret-button'); + // #endregion + + // #region View Options this.gridBtn = page.getByLabel('Grid'); this.listBtn = page.getByLabel('List'); this.viewBundleBtn = page.locator('umb-collection-view-bundle uui-button svg'); - this.createDocumentBlueprintModal = page.locator('umb-document-blueprint-options-create-modal'); - this.createDocumentBlueprintBtn = page.getByLabel(/^Create Document Blueprint(…)?$/); - this.createNewDocumentBlueprintBtn = this.createDocumentBlueprintModal.locator('umb-ref-item', {hasText: 'Document Blueprint for'}); - this.chooseDocumentInputBtn = page.locator('umb-input-document').getByLabel('Choose'); - this.chooseMediaInputBtn = page.locator('umb-input-media').getByLabel('Choose'); - this.container = page.locator('#container'); - this.actionBtn = page.getByTestId('workspace:action-menu-button'); + // #endregion + + // #region Media + this.mediaCardItems = page.locator('uui-card-media'); this.mediaPickerModalSubmitBtn = page.locator('umb-media-picker-modal').getByLabel('Submit'); - this.deleteBtn = page.getByRole('button', {name: /^Delete(…)?$/}); - this.createModalBtn = this.sidebarModal.getByLabel('Create', {exact: true}); this.mediaCaptionAltTextModalSubmitBtn = page.locator('umb-media-caption-alt-text-modal').getByLabel('Submit'); + this.clickToUploadBtn = page.locator('#splitViews').getByRole('button', {name: 'Click to upload'}); + this.inputDropzone = page.locator('umb-input-dropzone'); + this.imageCropperField = page.locator('umb-image-cropper-field'); + this.inputUploadField = page.locator('umb-input-upload-field').locator('#wrapperInner'); + this.chooseMediaInputBtn = page.locator('umb-input-media').getByLabel('Choose'); + // #endregion + + // #region Embedded Media this.embeddedMediaModal = page.locator('umb-embedded-media-modal'); - this.embeddedURLTxt = this.embeddedMediaModal.locator('[label="URL"] #input'); - this.embeddedRetrieveBtn = this.embeddedMediaModal.locator('[label="Retrieve"]'); - this.embeddedMediaModalConfirmBtn = this.embeddedMediaModal.getByLabel('Confirm'); - this.embeddedPreview = this.embeddedMediaModal.locator('[label="Preview"]'); - this.sectionSidebar = page.locator('umb-section-sidebar'); - this.menuItem = page.locator('uui-menu-item'); - this.property = page.locator('umb-property'); + this.embeddedURLTxt = page.locator('umb-embedded-media-modal').locator('[label="URL"] #input'); + this.embeddedRetrieveBtn = page.locator('umb-embedded-media-modal').locator('[label="Retrieve"]'); + this.embeddedMediaModalConfirmBtn = page.locator('umb-embedded-media-modal').getByLabel('Confirm'); + this.embeddedPreview = page.locator('umb-embedded-media-modal').locator('[label="Preview"]'); + // #endregion + + // #region Document & Content + this.chooseDocumentInputBtn = page.locator('umb-input-document').getByLabel('Choose'); + this.createDocumentBlueprintBtn = page.getByLabel(/^Create Document Blueprint(…)?$/); + this.createDocumentBlueprintModal = page.locator('umb-document-blueprint-options-create-modal'); + this.createNewDocumentBlueprintBtn = page.locator('umb-document-blueprint-options-create-modal').locator('umb-ref-item', {hasText: 'Document Blueprint for'}); + // #endregion + + // #region User this.currentUserAvatarBtn = page.getByTestId('header-app:Umb.HeaderApp.CurrentUser').locator('uui-avatar'); this.currentPasswordTxt = page.locator('input[name="oldPassword"]'); this.newPasswordTxt = page.locator('input[name="newPassword"]'); this.confirmPasswordTxt = page.locator('input[name="confirmPassword"]'); - this.createOptionActionListModal = page.locator('umb-entity-create-option-action-list-modal'); - this.createActionButtonCollection = page.locator('umb-collection-create-action-button'); - this.createActionBtn = this.createActionButtonCollection.locator('[label="Create"]'); + // #endregion + + // #region Collection & Table this.collectionTreeItemTableRow = page.locator('umb-collection-workspace-view umb-table uui-table-row'); - this.folderBtn = this.createOptionActionListModal.locator('umb-ref-item', {hasText: 'Folder'}); - this.reloadChildrenBtn = page.getByRole('button', {name: 'Reload children'}); + this.createActionButtonCollection = page.locator('umb-collection-create-action-button'); + this.createActionBtn = page.locator('umb-collection-create-action-button').locator('[label="Create"]'); + this.createOptionActionListModal = page.locator('umb-entity-create-option-action-list-modal'); + // #endregion + + // #region Reference & Entity this.confirmActionModalEntityReferences = page.locator('umb-confirm-action-modal-entity-references,umb-confirm-bulk-action-modal-entity-references'); - this.referenceHeadline = this.confirmActionModalEntityReferences.locator('#reference-headline').first(); - this.entityItemRef = this.confirmActionModalEntityReferences.locator('uui-ref-list').first().getByTestId('entity-item-ref'); - this.validationMessage = page.locator('umb-form-validation-message').locator('#messages'); - this.successStateIcon = this.successState.locator('#state'); + this.referenceHeadline = page.locator('umb-confirm-action-modal-entity-references,umb-confirm-bulk-action-modal-entity-references').locator('#reference-headline').first(); + this.entityItemRef = page.locator('umb-confirm-action-modal-entity-references,umb-confirm-bulk-action-modal-entity-references').locator('uui-ref-list').first().getByTestId('entity-item-ref'); + this.entityItem = page.locator('umb-entity-item-ref'); + // #endregion + + // #region Workspace & Action this.workspaceAction = page.locator('umb-workspace-action'); - this.caretBtn = page.locator('#caret-button'); - this.sectionLinks = page.getByTestId('section-links'); - // Entity Action - this.entityAction = page.locator('umb-entity-action-list umb-entity-action'); - this.openEntityAction = page.locator('#action-modal[open]').locator(this.entityAction); - // Workspace Entity Action this.workspaceActionMenuBtn = page.getByTestId('workspace:action-menu-button'); + this.entityAction = page.locator('umb-entity-action-list umb-entity-action'); + this.openEntityAction = page.locator('#action-modal[open]').locator(page.locator('umb-entity-action-list umb-entity-action')); + // #endregion + + // #region Pagination + this.firstPaginationBtn = page.locator('umb-collection-pagination').getByLabel('First'); + this.nextPaginationBtn = page.locator('umb-collection-pagination').getByLabel('Next'); + // #endregion + + // #region Editor this.monacoEditor = page.locator('.monaco-editor'); - this.openedModal = page.locator('uui-modal-container[backdrop]'); + // #endregion + + // #region Loader this.uiLoader = page.locator('uui-loader'); - this.inputDropzone = page.locator('umb-input-dropzone'); - this.imageCropperField = page.locator('umb-image-cropper-field'); - this.inputUploadField = page.locator('umb-input-upload-field').locator('#wrapperInner'); - this.entityItem = page.locator('umb-entity-item-ref'); - this.restoreBtn = page.getByLabel('Restore', {exact: true}); - this.backOfficeMain = page.locator('umb-backoffice-main'); + // #endregion } + // #region Actions Menu Methods async clickActionsMenuForNameInSectionSidebar(name: string) { await this.sectionSidebar.locator('[label="' + name + '"]').hover(); await this.sectionSidebar.locator('[label="' + name + '"] >> [label="Open actions menu"]').first().click(); @@ -338,7 +521,6 @@ export class UiBaseLocators extends BasePage { async clickActionsMenuForName(name: string) { await expect(this.page.locator('uui-menu-item[label="' + name + '"]').locator('#menu-item').first()).toBeVisible(); - // We need to wait for the load to be finished, otherwise we would run into flaky tests await this.page.waitForTimeout(ConstantHelper.wait.medium); await this.page.locator('uui-menu-item[label="' + name + '"]').locator('#menu-item').first().hover({force: true}); await this.page.locator('uui-menu-item[label="' + name + '"] #action-modal').first().click({force: true}); @@ -348,7 +530,9 @@ export class UiBaseLocators extends BasePage { await this.page.locator('uui-menu-item[label="' + name + '"]').click(); await expect(this.page.locator('uui-menu-item[label="' + name + '"] #action-modal').first()).toBeVisible({visible: isVisible}); } + // #endregion + // #region Caret Button Methods async clickCaretButtonForName(name: string) { await this.isCaretButtonWithNameVisible(name); await this.page.locator('uui-menu-item[label="' + name + '"]').locator('#caret-button').first().click(); @@ -370,22 +554,31 @@ export class UiBaseLocators extends BasePage { menuItem = this.page.locator('uui-menu-item[label="' + name + '"]'); } const isCaretButtonOpen = await menuItem.getAttribute('show-children'); - if (isCaretButtonOpen === null) { await this.clickCaretButtonForName(name); } } + // #endregion + // #region Tree Methods async reloadTree(treeName: string) { - // Waits until the tree item is visible await expect(this.page.getByLabel(treeName, {exact: true})).toBeVisible(); await this.page.waitForTimeout(ConstantHelper.wait.short); await this.clickActionsMenuForName(treeName); await this.clickReloadChildrenActionMenuOption(); - await this.openCaretButtonForName(treeName); } + async isTreeItemVisible(name: string, isVisible = true) { + await this.isVisible(this.page.locator('umb-tree-item').locator('[label="' + name + '"]'), isVisible); + } + + async doesTreeItemHaveTheCorrectIcon(name: string, icon: string) { + return await this.isVisible(this.page.locator('umb-tree-item').filter({hasText: name}).locator('umb-icon').locator('[name="' + icon + '"]')); + } + // #endregion + + // #region Core Button Click Methods async clickReloadButton() { await this.click(this.reloadBtn); } @@ -394,13 +587,6 @@ export class UiBaseLocators extends BasePage { await this.click(this.reloadChildrenBtn, {force: true}); } - async isSuccessStateVisibleForSaveButton(isVisible: boolean = true) { - const regex = new RegExp(`^workspace-action:.*Save$`); - const saveButtonLocator = this.page.getByTestId(regex); - const saveBtn = this.workspaceAction.filter({has: saveButtonLocator}); - await expect(saveBtn.locator(this.successState)).toBeVisible({visible: isVisible, timeout: ConstantHelper.timeout.long}); - } - async clickSaveButton() { await this.click(this.saveBtn); await this.page.waitForTimeout(ConstantHelper.wait.short); @@ -414,236 +600,264 @@ export class UiBaseLocators extends BasePage { await this.click(this.containerChooseBtn); } - async clickFilterChooseButton() { - await this.click(this.filterChooseBtn); - } - - async clickRenameFolderThreeDotsButton() { - await this.click(this.renameFolderThreeDotsBtn); + async clickSubmitButton() { + await this.click(this.submitBtn); } - async clickRenameFolderButton() { - await this.clickRenameButton(); + async clickConfirmButton() { + await this.click(this.confirmBtn); } - async clickConfirmRenameButton() { - await this.click(this.confirmRenameBtn); + async clickCreateButton() { + await this.click(this.createBtn); } - async clickUpdateFolderButton() { - await this.click(this.updateFolderBtn); + async clickAddButton() { + await this.click(this.addBtn); } async clickUpdateButton() { await this.click(this.updateBtn); } - async clickSubmitButton() { - await this.click(this.submitBtn); + async clickChangeButton() { + await this.click(this.changeBtn); } - async clickConfirmToSubmitButton() { - await this.click(this.confirmToSubmitBtn); + async clickDeleteButton() { + await this.click(this.deleteBtn); } - async clickChangeButton() { - await this.click(this.changeBtn); + async clickDeleteExactButton() { + await this.click(this.deleteExactBtn); } - async clickExactLinkWithName(name: string, toForce: boolean = false) { - const exactLinkWithNameLocator = this.page.getByRole('link', {name: name, exact: true}); - await this.click(exactLinkWithNameLocator, {force: toForce}); + async clickRemoveExactButton() { + await this.click(this.removeExactBtn); } - async enterAliasName(aliasName: string) { - // Unlocks alias - await this.page.waitForTimeout(ConstantHelper.wait.short); - await this.click(this.aliasLockBtn, {force: true}); - await this.enterText(this.aliasNameTxt, aliasName); + async clickInsertButton() { + await this.click(this.insertBtn); } - async updateIcon(iconName: string) { - // Force click is needed - await this.click(this.iconBtn, {force: true}); - await this.searchForTypeToFilterValue(iconName); - await this.clickLabelWithName(iconName, true, true); - await this.clickSubmitButton(); + async clickRenameButton() { + await this.click(this.renameBtn); } - async clickTextButtonWithName(name: string) { - await this.click(this.page.getByText(name, {exact: true})); + async clickRestoreButton() { + await this.click(this.restoreBtn); } - async clickSelectPropertyEditorButton() { - await this.click(this.selectPropertyEditorBtn); + async clickDisableButton() { + await this.click(this.disableBtn); } - async clickCreateFolderButton() { - await this.click(this.createFolderBtn); + async clickEnableButton() { + await this.click(this.enableBtn); + } + + async clickActionButton() { + await this.click(this.actionBtn); + } + + async clickNextButton() { + await this.click(this.nextBtn); + } + + async clickBreadcrumbButton() { + await this.click(this.breadcrumbBtn); + } + + async clickLeftArrowButton() { + await this.click(this.leftArrowBtn); + } + // #endregion + + // #region Confirmation Button Methods + async clickConfirmToDeleteButton() { + await this.click(this.confirmToDeleteBtn); await this.page.waitForTimeout(ConstantHelper.wait.short); } - async enterAPropertyName(name: string) { - await this.enterText(this.propertyNameTxt, name, {clearFirst: false}); + async clickConfirmCreateFolderButton() { + await this.click(this.confirmCreateFolderBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } - async clickNextPaginationButton() { - await this.click(this.nextPaginationBtn); + async clickConfirmRemoveButton() { + await this.click(this.confirmToRemoveBtn); + } + + async clickConfirmToSubmitButton() { + await this.click(this.confirmToSubmitBtn); + } + + async clickConfirmDisableButton() { + await this.click(this.confirmDisableBtn); + } + + async clickConfirmEnableButton() { + await this.click(this.confirmEnableBtn); + } + + async clickConfirmRenameButton() { + await this.click(this.confirmRenameBtn); } - async clickNextButton(){ - await this.click(this.nextBtn); + async clickConfirmTrashButton() { + await this.click(this.confirmTrashBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } - async clickConfirmButton() { - await this.click(this.confirmBtn); + async clickDeleteAndConfirmButton() { + await this.clickDeleteActionMenuOption(); + await this.clickConfirmToDeleteButton(); } + // #endregion - async clickBreadcrumbButton() { - await this.click(this.breadcrumbBtn); + // #region Folder Methods + async clickCreateFolderButton() { + await this.click(this.createFolderBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } - async clickInsertButton() { - await this.click(this.insertBtn); + async enterFolderName(folderName: string) { + await this.enterText(this.folderNameTxt, folderName, {verify: true}); } - async clickConfirmToDeleteButton() { - await this.click(this.confirmToDeleteBtn); - await this.page.waitForTimeout(ConstantHelper.wait.short); + async createFolder(folderName: string) { + await this.clickCreateActionMenuOption(); + await this.clickNewFolderThreeDotsButton(); + await this.enterFolderName(folderName); + await this.clickConfirmCreateFolderButton(); } - async clickConfirmCreateFolderButton() { - await this.click(this.confirmCreateFolderBtn); - await this.page.waitForTimeout(ConstantHelper.wait.short); + async deleteFolder() { + await this.clickDeleteActionMenuOption(); + await this.clickConfirmToDeleteButton(); } - async clickRemoveExactButton() { - await this.click(this.removeExactBtn); + async clickFolderButton() { + await this.click(this.folderBtn); } - async clickRemoveButtonForName(name: string) { - const removeButtonWithNameLocator = this.page.locator('[name="' + name + '"] [label="Remove"]'); - await this.click(removeButtonWithNameLocator); + async clickNewFolderThreeDotsButton() { + await this.click(this.newFolderThreeDotsBtn); } - async clickTrashIconButtonForName(name: string) { - const trashIconButtonWithNameLocator = this.page.locator('[name="' + name + '"] [name="icon-trash"]'); - await this.click(trashIconButtonWithNameLocator); + async clickRenameFolderThreeDotsButton() { + await this.click(this.renameFolderThreeDotsBtn); } - async clickRemoveWithName(name: string) { - const removeLabelWithNameLocator = this.page.locator('[label="Remove ' + name + '"]'); - await this.click(removeLabelWithNameLocator); + async clickRenameFolderButton() { + await this.clickRenameButton(); } - async clickDisableButton() { - await this.click(this.disableBtn); + async clickUpdateFolderButton() { + await this.click(this.updateFolderBtn); } + // #endregion - async clickConfirmDisableButton() { - await this.click(this.confirmDisableBtn); + // #region Three Dots Menu Methods + async clickCreateThreeDotsButton() { + await this.click(this.createThreeDotsBtn); } - async clickConfirmRemoveButton() { - await this.click(this.confirmToRemoveBtn); + async clickFilterChooseButton() { + await this.click(this.filterChooseBtn); } + // #endregion - async clickEnableButton() { - await this.click(this.enableBtn); + // #region Success State Methods + async isSuccessStateVisibleForSaveButton(isVisible: boolean = true) { + const regex = new RegExp(`^workspace-action:.*Save$`); + const saveButtonLocator = this.page.getByTestId(regex); + const saveBtn = this.workspaceAction.filter({has: saveButtonLocator}); + await expect(saveBtn.locator(this.successState)).toBeVisible({visible: isVisible, timeout: ConstantHelper.timeout.long}); } - async clickConfirmEnableButton() { - await this.click(this.confirmEnableBtn); + async isSuccessButtonWithTextVisible(text: string) { + return await this.isVisible(this.successState.filter({hasText: text})); } - async insertDictionaryItem(dictionaryName: string) { - await this.clickInsertButton(); - await this.click(this.insertDictionaryItemBtn); - await this.click(this.page.getByLabel(dictionaryName)); - await this.click(this.chooseBtn); + async isSuccessStateIconVisible() { + await this.isVisible(this.successStateIcon); } - async addQueryBuilderWithOrderByStatement(propertyAlias: string, isAscending: boolean) { - await this.click(this.queryBuilderBtn, {timeout: ConstantHelper.timeout.long}); - await this.click(this.orderByPropertyAliasBtn); - // Wait and choose property alias option - await this.waitAndSelectQueryBuilderDropDownList(propertyAlias); - await this.click(this.orderByPropertyAliasBtn); - // Click to ascending button if isAscending is false - if (!isAscending) { - await this.click(this.ascendingBtn); - } + async isFailedStateButtonVisible() { + await this.isVisible(this.failedStateButton); } + // #endregion - async addQueryBuilderWithWhereStatement(propertyAlias: string, operator: string, constrainValue: string) { - await this.click(this.queryBuilderBtn, {timeout: ConstantHelper.timeout.long}); - // Wait and choose property alias - await this.click(this.wherePropertyAliasBtn); - await this.waitAndSelectQueryBuilderDropDownList(propertyAlias); - // Wait and choose operator - await this.click(this.whereOperatorBtn); - await this.waitAndSelectQueryBuilderDropDownList(operator); - // Wait and choose constrain value and press Enter - await this.enterText(this.whereConstrainValueTxt, constrainValue); - await this.pressKey(this.whereConstrainValueTxt, 'Enter'); + // #region Notification Methods + async isSuccessNotificationVisible(isVisible: boolean = true) { + return await expect(this.successNotification.first()).toBeVisible({visible: isVisible, timeout: ConstantHelper.timeout.long}); } - async waitAndSelectQueryBuilderDropDownList(option: string) { - const ddlOption = this.page.locator('[open]').locator('uui-combobox-list-option').filter({hasText: option}).first(); - await this.click(ddlOption, {timeout: ConstantHelper.timeout.long}); + async doesSuccessNotificationsHaveCount(count: number) { + return await expect(this.successNotification).toHaveCount(count); } - async createFolder(folderName: string) { - await this.clickCreateActionMenuOption(); - await this.clickNewFolderThreeDotsButton(); - await this.enterFolderName(folderName); - await this.clickConfirmCreateFolderButton(); + async doesSuccessNotificationHaveText(text: string, isVisible: boolean = true, deleteNotification = false, timeout = 5000) { + const response = await expect(this.successNotification.filter({hasText: text})).toBeVisible({ + visible: isVisible, + timeout: timeout + }); + if (deleteNotification) { + await this.successNotification.filter({hasText: text}).getByLabel('close').click({force: true}); + } + return response; } - async deletePropertyEditor(propertyEditorName: string) { - // We need to hover over the property to be able to see the delete button - await this.page.locator('uui-button').filter({hasText: propertyEditorName}).getByLabel('Editor settings').hover(); - await this.deleteBtn.click(); + async isErrorNotificationVisible(isVisible: boolean = true) { + return await expect(this.errorNotification.first()).toBeVisible({visible: isVisible}); } - async enterFolderName(folderName: string) { - await this.enterText(this.folderNameTxt, folderName, {verify: true}); + async doesErrorNotificationHaveText(text: string, isVisible: boolean = true, deleteNotification: boolean = false) { + const response = await expect(this.errorNotification.filter({hasText: text})).toBeVisible({visible: isVisible}); + if (deleteNotification) { + await this.errorNotification.filter({hasText: text}).locator('svg').click(); + } + return response; } + // #endregion - async isTextWithExactNameVisible(name: string, isVisible = true) { - return expect(this.page.getByText(name, {exact: true})).toBeVisible({visible: isVisible}); + // #region Modal Methods + async clickChooseModalButton() { + await this.click(this.chooseModalBtn); } - async isQueryBuilderCodeShown(code: string) { - await this.click(this.queryBuilderShowCode); - await this.containsText(this.queryBuilderShowCode, code, 10000); + async clickCreateModalButton() { + await this.click(this.createModalBtn); } - async deleteFolder() { - await this.clickDeleteActionMenuOption(); - await this.clickConfirmToDeleteButton(); + async clickModalMenuItemWithName(name: string) { + await this.click(this.openedModal.locator('uui-menu-item[label="' + name + '"]')); } - async clickDeleteExactButton() { - await this.click(this.deleteExactBtn); + async isModalMenuItemWithNameDisabled(name: string) { + await this.hasAttribute(this.sidebarModal.locator('uui-menu-item[label="' + name + '"]'), 'disabled', ''); } - async isTreeItemVisible(name: string, isVisible = true) { - await this.isVisible(this.page.locator('umb-tree-item').locator('[label="' + name + '"]'), isVisible); + async isModalMenuItemWithNameVisible(name: string, isVisible: boolean = true) { + await this.isVisible(this.sidebarModal.locator('uui-menu-item[label="' + name + '"]'), isVisible); } + // #endregion - async doesTreeItemHaveTheCorrectIcon(name: string, icon: string) { - return await this.isVisible(this.page.locator('umb-tree-item').filter({hasText: name}).locator('umb-icon').locator('[name="' + icon + '"]')); + // #region Container Methods + async clickContainerSaveAndPublishButton() { + await this.click(this.containerSaveAndPublishBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } + // #endregion + // #region Navigation Methods async goToSection(sectionName: string, checkSections = true, skipReload = false) { if (checkSections) { for (let section in ConstantHelper.sections) { await expect(this.sectionLinks.getByRole('tab', {name: ConstantHelper.sections[section]})).toBeVisible({timeout: ConstantHelper.timeout.navigation}); } } - - // We need to check if we are on the section tab already, if we are, then we need to reload the page instead of clicking again const alreadySelected = await this.sectionLinks.locator('[active]').getByText(sectionName).isVisible(); if (alreadySelected && !skipReload) { await this.page.reload(); @@ -657,16 +871,24 @@ export class UiBaseLocators extends BasePage { await this.click(this.page.getByLabel(settingsTreeItemName, {exact: true})); } - async clickDataElement(elementName: string, options: any = null) { - await this.page.click(`[data-element="${elementName}"]`, options); + async isSectionWithNameVisible(sectionName: string, isVisible: boolean = true) { + await this.isVisible(this.page.getByRole('tab', {name: sectionName}), isVisible); } - async getDataElement(elementName: string) { - return this.page.locator(`[data-element="${elementName}"]`); + async isBackOfficeMainVisible(isVisible: boolean = true) { + await this.page.waitForTimeout(ConstantHelper.wait.medium); + await this.isVisible(this.backOfficeMain, isVisible); } + // #endregion - async isButtonWithNameVisible(name: string) { - await this.isVisible(this.page.getByRole('button', {name: name})); + // #region Link & Button Click by Name Methods + async clickExactLinkWithName(name: string, toForce: boolean = false) { + const exactLinkWithNameLocator = this.page.getByRole('link', {name: name, exact: true}); + await this.click(exactLinkWithNameLocator, {force: toForce}); + } + + async clickLinkWithName(name: string, isExact: boolean = false) { + await this.click(this.page.getByRole('link', {name: name, exact: isExact})); } async clickLabelWithName(name: string, isExact: boolean = true, toForce: boolean = false) { @@ -675,111 +897,66 @@ export class UiBaseLocators extends BasePage { async clickButtonWithName(name: string, isExact: boolean = false) { const exactButtonWithNameLocator = this.page.getByRole('button', {name: name, exact: isExact}); - // Force click is needed await this.click(exactButtonWithNameLocator, {force: true}); } - async isSuccessNotificationVisible(isVisible: boolean = true) { - return await expect(this.successNotification.first()).toBeVisible({visible: isVisible, timeout: ConstantHelper.timeout.long}); - } - - async doesSuccessNotificationsHaveCount(count: number) { - return await expect(this.successNotification).toHaveCount(count); - } - - async isErrorNotificationVisible(isVisible: boolean = true) { - return await expect(this.errorNotification.first()).toBeVisible({visible: isVisible}); - } - - async isTextWithMessageVisible(message: string, isVisible: boolean = true) { - return await expect(this.page.getByText(message)).toBeVisible({visible: isVisible}); - } - - async clickCreateThreeDotsButton() { - await this.click(this.createThreeDotsBtn); - } - - async clickCreateButton() { - await this.click(this.createBtn); - } - - async clickAddButton() { - await this.click(this.addBtn); - }; - - async clickNewFolderThreeDotsButton() { - await this.click(this.newFolderThreeDotsBtn); - } - - async clickEditorSettingsButton(index: number = 0) { - await this.click(this.editorSettingsBtn.nth(index)); - } - - async enterDescription(description: string) { - await this.enterText(this.enterDescriptionTxt, description); - } - - async doesDescriptionHaveValue(value: string, index: number = 0) { - return await this.hasValue(this.descriptionBtn.nth(index), value); - } - - async clickStructureTab() { - await this.page.waitForTimeout(ConstantHelper.wait.medium); - await this.click(this.structureTabBtn); - } - - async clickAllowAtRootButton() { - await this.click(this.allowAtRootBtn); - } - - async clickIAmDoneReorderingButton() { - await this.click(this.iAmDoneReorderingBtn); + async clickTextButtonWithName(name: string) { + await this.click(this.page.getByText(name, {exact: true})); } - async clickReorderButton() { - await this.click(this.reorderBtn); + async isButtonWithNameVisible(name: string) { + await this.isVisible(this.page.getByRole('button', {name: name})); } - async clickLabelAboveButton() { - await this.click(this.labelAboveBtn); + async getButtonWithName(name: string) { + await this.waitForVisible(this.page.getByRole('button', {name: name})); + return this.page.getByRole('button', {name: name}); } + // #endregion - async clickMandatoryToggle() { - await this.click(this.mandatoryToggle); + // #region Remove Button Methods + async clickRemoveButtonForName(name: string) { + const removeButtonWithNameLocator = this.page.locator('[name="' + name + '"] [label="Remove"]'); + await this.click(removeButtonWithNameLocator); } - async selectValidationOption(option: string) { - await this.selectByValue(this.validation, option); + async clickTrashIconButtonForName(name: string) { + const trashIconButtonWithNameLocator = this.page.locator('[name="' + name + '"] [name="icon-trash"]'); + await this.click(trashIconButtonWithNameLocator); } - async enterRegEx(regEx: string) { - await this.enterText(this.regexTxt, regEx, {clearFirst: false}); + async clickRemoveWithName(name: string) { + const removeLabelWithNameLocator = this.page.locator('[label="Remove ' + name + '"]'); + await this.click(removeLabelWithNameLocator); } + // #endregion - async enterRegExMessage(regExMessage: string) { - await this.enterText(this.regexMessageTxt, regExMessage, {clearFirst: false}); + // #region Alias & Icon Methods + async enterAliasName(aliasName: string) { + await this.page.waitForTimeout(ConstantHelper.wait.short); + await this.click(this.aliasLockBtn, {force: true}); + await this.enterText(this.aliasNameTxt, aliasName); } - async clickCompositionsButton() { - await this.click(this.compositionsBtn); + async updateIcon(iconName: string) { + await this.click(this.iconBtn, {force: true}); + await this.searchForTypeToFilterValue(iconName); + await this.clickLabelWithName(iconName, true, true); + await this.clickSubmitButton(); } + // #endregion - async clickAddTabButton() { - await this.click(this.addTabBtn); + // #region Property Editor Methods + async clickSelectPropertyEditorButton() { + await this.click(this.selectPropertyEditorBtn); } - async enterTabName(tabName: string) { - await this.waitForVisible(this.unnamedTabTxt); - await this.page.waitForTimeout(ConstantHelper.wait.debounce); - await this.enterText(this.unnamedTabTxt, tabName); - // We use this to make sure the test id is updated - await this.page.getByRole('tab', {name: 'Design'}).click(); - // We click again to make sure the tab is focused - await this.page.getByTestId('tab:' + tabName).click(); + async enterAPropertyName(name: string) { + await this.enterText(this.propertyNameTxt, name, {clearFirst: false}); } - async searchForTypeToFilterValue(searchValue: string) { - await this.enterText(this.typeToFilterSearchTxt, searchValue, {clearFirst: false}); + async clickEditorSettingsButton(index: number = 0) { + await this.click(this.editorSettingsBtn.nth(index)); } async addPropertyEditor(propertyEditorName: string, index: number = 0) { @@ -801,16 +978,40 @@ export class UiBaseLocators extends BasePage { await this.clickSubmitButton(); } + async deletePropertyEditor(propertyEditorName: string) { + await this.page.locator('uui-button').filter({hasText: propertyEditorName}).getByLabel('Editor settings').hover(); + await this.deleteBtn.click(); + } + + async deletePropertyEditorWithName(name: string) { + const propertyEditor = this.page.locator('umb-content-type-design-editor-property', {hasText: name}); + await this.hover(propertyEditor); + await this.click(propertyEditor.getByLabel('Delete'), {force: true}); + await this.clickConfirmToDeleteButton(); + } + async enterPropertyEditorDescription(description: string) { await this.enterText(this.enterPropertyEditorDescriptionTxt, description); } - async clickAddGroupButton() { - await this.click(this.addGroupBtn); + async isPropertyEditorUiWithNameReadOnly(name: string) { + const propertyEditorUiLocator = this.page.locator('umb-property-editor-ui-' + name); + await this.hasAttribute(propertyEditorUiLocator, 'readonly', ''); } - async clickChooseModalButton() { - await this.click(this.chooseModalBtn); + async isPropertyEditorUiWithNameVisible(name: string, isVisible: boolean = true) { + const propertyEditorUiLocator = this.page.locator('umb-property-editor-ui-' + name); + await this.isVisible(propertyEditorUiLocator, isVisible); + } + + async doesPropertyHaveInvalidBadge(propertyName: string) { + await this.isVisible(this.page.locator('umb-property-layout').filter({hasText: propertyName}).locator('#invalid-badge uui-badge')); + } + // #endregion + + // #region Group Methods + async clickAddGroupButton() { + await this.click(this.addGroupBtn); } async enterGroupName(groupName: string, index: number = 0) { @@ -827,102 +1028,89 @@ export class UiBaseLocators extends BasePage { return await this.hasValue(this.groupLabel, value); } - async rename(newName: string) { - await this.clickRenameActionMenuOption(); - await this.click(this.newNameTxt); - await this.enterText(this.newNameTxt, newName); - await this.click(this.renameModalBtn); - await this.page.waitForTimeout(ConstantHelper.wait.short); + async deleteGroup(groupName: string) { + await this.page.waitForTimeout(ConstantHelper.wait.medium); + const groups = this.page.locator('umb-content-type-design-editor-group').all(); + for (const group of await groups) { + if (await group.getByLabel('Group', {exact: true}).inputValue() === groupName) { + const headerActionsDeleteLocator = group.locator('[slot="header-actions"]').getByLabel('Delete'); + await this.click(headerActionsDeleteLocator, {force: true}); + return; + } + } } - async isSuccessButtonWithTextVisible(text: string) { - return await this.isVisible(this.successState.filter({hasText: text})); + async reorderTwoGroups(firstGroupName: string, secondGroupName: string) { + const firstGroup = this.page.getByTestId('group:' + firstGroupName); + const secondGroup = this.page.getByTestId('group:' + secondGroupName); + const firstGroupValue = await firstGroup.getByLabel('Group').inputValue(); + const secondGroupValue = await secondGroup.getByLabel('Group').inputValue(); + const dragToLocator = firstGroup.locator('[slot="header"]').first(); + const dragFromLocator = secondGroup.locator('[slot="header"]').first(); + await this.dragAndDrop(dragFromLocator, dragToLocator, 0, 0, 20); + return {firstGroupValue, secondGroupValue}; } + // #endregion - async dragAndDrop(dragFromSelector: Locator, dragToSelector: Locator, verticalOffset: number = 0, horizontalOffset: number = 0, steps: number = 5) { - await this.waitForVisible(dragFromSelector); - await this.waitForVisible(dragToSelector); - const targetLocation = await dragToSelector.boundingBox(); - const elementCenterX = targetLocation!.x + targetLocation!.width / 2; - const elementCenterY = targetLocation!.y + targetLocation!.height / 2; - await this.hover(dragFromSelector); - await this.page.mouse.move(10, 10); - await this.hover(dragFromSelector); - await this.page.mouse.down(); - await this.page.waitForTimeout(ConstantHelper.wait.debounce); - await this.page.mouse.move(elementCenterX + horizontalOffset, elementCenterY + verticalOffset, {steps: steps}); - await this.page.waitForTimeout(ConstantHelper.wait.debounce); - await this.page.mouse.up(); + // #region Tab Methods + async clickAddTabButton() { + await this.click(this.addTabBtn); } - async getButtonWithName(name: string) { - await this.waitForVisible(this.page.getByRole('button', {name: name})); - return this.page.getByRole('button', {name: name}); + async enterTabName(tabName: string) { + await this.waitForVisible(this.unnamedTabTxt); + await this.page.waitForTimeout(ConstantHelper.wait.debounce); + await this.enterText(this.unnamedTabTxt, tabName); + await this.page.getByRole('tab', {name: 'Design'}).click(); + await this.page.getByTestId('tab:' + tabName).click(); } - async clickCreateLink() { - await this.click(this.createLink); + async clickRemoveTabWithName(name: string) { + const tab = this.page.locator('uui-tab').filter({hasText: name}); + await this.hover(tab); + const removeTabWithNameLocator = tab.locator('[label="Remove"]'); + await this.click(removeTabWithNameLocator); } - async insertSystemFieldValue(fieldValue: string) { - await this.clickInsertButton(); - await this.click(this.insertValueBtn); - await this.click(this.chooseFieldDropDown); - await this.click(this.systemFieldsOption); - await this.click(this.chooseFieldValueDropDown); - await this.click(this.page.getByText(fieldValue)); - await this.clickSubmitButton(); + async clickStructureTab() { + await this.page.waitForTimeout(ConstantHelper.wait.medium); + await this.click(this.structureTabBtn); } - async insertPartialView(partialViewName: string) { - await this.clickInsertButton(); - await this.click(this.insertPartialViewBtn); - await this.click(this.page.getByLabel(partialViewName)); - await this.clickChooseButton(); + getTabLocatorWithName(name: string) { + return this.page.getByRole('tab', {name: name}); } + // #endregion - async deletePropertyEditorWithName(name: string) { - // We need to hover over the Property Editor to make the delete button visible - const propertyEditor = this.page.locator('umb-content-type-design-editor-property', {hasText: name}); - await this.hover(propertyEditor); - // Force click is needed - await this.click(propertyEditor.getByLabel('Delete'), {force: true}); - await this.clickConfirmToDeleteButton(); + // #region Validation Methods + async clickMandatoryToggle() { + await this.click(this.mandatoryToggle); } - async clickRenameButton() { - await this.click(this.renameBtn); + async selectValidationOption(option: string) { + await this.selectByValue(this.validation, option); } - async clickDeleteAndConfirmButton() { - await this.clickDeleteActionMenuOption(); - await this.clickConfirmToDeleteButton(); + async enterRegEx(regEx: string) { + await this.enterText(this.regexTxt, regEx, {clearFirst: false}); } - async clickDeleteButton() { - await this.click(this.deleteBtn); + async enterRegExMessage(regExMessage: string) { + await this.enterText(this.regexMessageTxt, regExMessage, {clearFirst: false}); } - async clickQueryBuilderButton() { - await this.click(this.queryBuilderBtn); - await this.page.waitForTimeout(ConstantHelper.wait.short); + async isValidationMessageVisible(message: string, isVisible: boolean = true) { + await this.isVisible(this.validationMessage.filter({hasText: message}), isVisible); } + // #endregion - async chooseRootContentInQueryBuilder(contentName: string) { - await this.click(this.chooseRootContentBtn); - await this.clickModalMenuItemWithName(contentName); - await this.clickChooseButton(); + // #region Composition & Structure Methods + async clickCompositionsButton() { + await this.click(this.compositionsBtn); } - async reorderTwoGroups(firstGroupName: string, secondGroupName: string) { - const firstGroup = this.page.getByTestId('group:' + firstGroupName); - const secondGroup = this.page.getByTestId('group:' + secondGroupName); - const firstGroupValue = await firstGroup.getByLabel('Group').inputValue(); - const secondGroupValue = await secondGroup.getByLabel('Group').inputValue(); - const dragToLocator = firstGroup.locator('[slot="header"]').first(); - const dragFromLocator = secondGroup.locator('[slot="header"]').first(); - await this.dragAndDrop(dragFromLocator, dragToLocator, 0, 0, 20); - return {firstGroupValue, secondGroupValue}; + async clickAllowAtRootButton() { + await this.click(this.allowAtRootBtn); } async clickAllowedChildNodesButton() { @@ -932,6 +1120,63 @@ export class UiBaseLocators extends BasePage { async clickAddCollectionButton() { await this.click(this.addCollectionBtn); } + // #endregion + + // #region Reorder Methods + async clickIAmDoneReorderingButton() { + await this.click(this.iAmDoneReorderingBtn); + } + + async clickReorderButton() { + await this.click(this.reorderBtn); + } + + async clickLabelAboveButton() { + await this.click(this.labelAboveBtn); + } + // #endregion + + // #region Query Builder Methods + async clickQueryBuilderButton() { + await this.click(this.queryBuilderBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); + } + + async addQueryBuilderWithOrderByStatement(propertyAlias: string, isAscending: boolean) { + await this.click(this.queryBuilderBtn, {timeout: ConstantHelper.timeout.long}); + await this.click(this.orderByPropertyAliasBtn); + await this.waitAndSelectQueryBuilderDropDownList(propertyAlias); + await this.click(this.orderByPropertyAliasBtn); + if (!isAscending) { + await this.click(this.ascendingBtn); + } + } + + async addQueryBuilderWithWhereStatement(propertyAlias: string, operator: string, constrainValue: string) { + await this.click(this.queryBuilderBtn, {timeout: ConstantHelper.timeout.long}); + await this.click(this.wherePropertyAliasBtn); + await this.waitAndSelectQueryBuilderDropDownList(propertyAlias); + await this.click(this.whereOperatorBtn); + await this.waitAndSelectQueryBuilderDropDownList(operator); + await this.enterText(this.whereConstrainValueTxt, constrainValue); + await this.pressKey(this.whereConstrainValueTxt, 'Enter'); + } + + async waitAndSelectQueryBuilderDropDownList(option: string) { + const ddlOption = this.page.locator('[open]').locator('uui-combobox-list-option').filter({hasText: option}).first(); + await this.click(ddlOption, {timeout: ConstantHelper.timeout.long}); + } + + async chooseRootContentInQueryBuilder(contentName: string) { + await this.click(this.chooseRootContentBtn); + await this.clickModalMenuItemWithName(contentName); + await this.clickChooseButton(); + } + + async isQueryBuilderCodeShown(code: string) { + await this.click(this.queryBuilderShowCode); + await this.containsText(this.queryBuilderShowCode, code, 10000); + } async doesReturnedItemsHaveCount(itemCount: number) { await this.containsText(this.returnedItemsCount, itemCount.toString() + ' published items returned'); @@ -940,95 +1185,110 @@ export class UiBaseLocators extends BasePage { async doesQueryResultHaveContentName(contentName: string) { await this.containsText(this.queryResults, contentName); } + // #endregion - async deleteGroup(groupName: string) { - await this.page.waitForTimeout(ConstantHelper.wait.medium); - const groups = this.page.locator('umb-content-type-design-editor-group').all(); - for (const group of await groups) { - if (await group.getByLabel('Group', {exact: true}).inputValue() === groupName) { - const headerActionsDeleteLocator = group.locator('[slot="header-actions"]').getByLabel('Delete'); - // Force click is needed - await this.click(headerActionsDeleteLocator, {force: true}); - return; - } - } + // #region Insert Methods + async insertDictionaryItem(dictionaryName: string) { + await this.clickInsertButton(); + await this.click(this.insertDictionaryItemBtn); + await this.click(this.page.getByLabel(dictionaryName)); + await this.click(this.chooseBtn); } - async clickRemoveTabWithName(name: string) { - const tab = this.page.locator('uui-tab').filter({hasText: name}); - await this.hover(tab); - const removeTabWithNameLocator = tab.locator('[label="Remove"]'); - await this.click(removeTabWithNameLocator); + async insertSystemFieldValue(fieldValue: string) { + await this.clickInsertButton(); + await this.click(this.insertValueBtn); + await this.click(this.chooseFieldDropDown); + await this.click(this.systemFieldsOption); + await this.click(this.chooseFieldValueDropDown); + await this.click(this.page.getByText(fieldValue)); + await this.clickSubmitButton(); } - async clickLeftArrowButton() { - await this.click(this.leftArrowBtn); + async insertPartialView(partialViewName: string) { + await this.clickInsertButton(); + await this.click(this.insertPartialViewBtn); + await this.click(this.page.getByLabel(partialViewName)); + await this.clickChooseButton(); } + // #endregion - async clickToUploadButton() { - await this.click(this.clickToUploadBtn); + // #region Rename Methods + async rename(newName: string) { + await this.clickRenameActionMenuOption(); + await this.click(this.newNameTxt); + await this.enterText(this.newNameTxt, newName); + await this.click(this.renameModalBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } + // #endregion - async uploadFile(filePath: string) { - const [fileChooser] = await Promise.all([ - this.page.waitForEvent('filechooser'), - await this.clickToUploadButton(), - ]); - await fileChooser.setFiles(filePath); + // #region Search & Filter Methods + async searchForTypeToFilterValue(searchValue: string) { + await this.enterText(this.typeToFilterSearchTxt, searchValue, {clearFirst: false}); } + // #endregion - getTabLocatorWithName(name: string) { - return this.page.getByRole('tab', {name: name}); + // #region Description Methods + async enterDescription(description: string) { + await this.enterText(this.enterDescriptionTxt, description); } - getTextLocatorWithName(name: string) { - return this.page.getByText(name, {exact: true}); + async doesDescriptionHaveValue(value: string, index: number = 0) { + return await this.hasValue(this.descriptionBtn.nth(index), value); } + // #endregion - getLocatorWithDataMark(dataMark: string) { - return this.page.getByTestId(dataMark); - }; - - async isFailedStateButtonVisible() { - await this.isVisible(this.failedStateButton); + // #region Drag and Drop Methods + async dragAndDrop(dragFromSelector: Locator, dragToSelector: Locator, verticalOffset: number = 0, horizontalOffset: number = 0, steps: number = 5) { + await this.waitForVisible(dragFromSelector); + await this.waitForVisible(dragToSelector); + const targetLocation = await dragToSelector.boundingBox(); + const elementCenterX = targetLocation!.x + targetLocation!.width / 2; + const elementCenterY = targetLocation!.y + targetLocation!.height / 2; + await this.hover(dragFromSelector); + await this.page.mouse.move(10, 10); + await this.hover(dragFromSelector); + await this.page.mouse.down(); + await this.page.waitForTimeout(ConstantHelper.wait.debounce); + await this.page.mouse.move(elementCenterX + horizontalOffset, elementCenterY + verticalOffset, {steps: steps}); + await this.page.waitForTimeout(ConstantHelper.wait.debounce); + await this.page.mouse.up(); } + // #endregion - async clickContainerSaveAndPublishButton() { - await this.click(this.containerSaveAndPublishBtn); - await this.page.waitForTimeout(ConstantHelper.wait.short); + // #region Create Link Methods + async clickCreateLink() { + await this.click(this.createLink); } + // #endregion - async clickConfirmTrashButton() { - await this.click(this.confirmTrashBtn); - await this.page.waitForTimeout(ConstantHelper.wait.short); + // #region Recycle Bin Methods + async clickRecycleBinButton() { + await this.click(this.recycleBinBtn); } async reloadRecycleBin(containsItems = true) { await this.waitForVisible(this.recycleBinMenuItem); - // If the Recycle Bin does not contain any items, the caret button should not be visible. and we should not try to click it if (!containsItems) { await this.clickReloadChildrenActionMenuOption(); await this.isVisible(this.recycleBinMenuItemCaretBtn, false); return; } - await this.clickActionsMenuForName('Recycle Bin'); await this.clickReloadChildrenActionMenuOption(); - await this.openCaretButtonForName('Recycle Bin'); } - async clickRecycleBinButton() { - await this.click(this.recycleBinBtn); - } - async isItemVisibleInRecycleBin(item: string, isVisible: boolean = true, isReload: boolean = true) { if (isReload) { await this.reloadRecycleBin(isVisible); } return await this.isVisible(this.page.locator('[label="Recycle Bin"] [label="' + item + '"]'), isVisible); } + // #endregion + // #region View Methods async changeToGridView() { await this.click(this.viewBundleBtn); await this.click(this.gridBtn); @@ -1042,79 +1302,83 @@ export class UiBaseLocators extends BasePage { async isViewBundleButtonVisible(isVisible: boolean = true) { return this.isVisible(this.viewBundleBtn, isVisible); } + // #endregion - async doesSuccessNotificationHaveText(text: string, isVisible: boolean = true, deleteNotification = false, timeout = 5000) { - const response = await expect(this.successNotification.filter({hasText: text})).toBeVisible({ - visible: isVisible, - timeout: timeout - }); - if (deleteNotification) { - await this.successNotification.filter({hasText: text}).getByLabel('close').click({force: true}); - } - return response; + // #region Media Methods + async clickMediaWithName(name: string) { + await this.click(this.mediaCardItems.filter({hasText: name})); } - async doesErrorNotificationHaveText(text: string, isVisible: boolean = true, deleteNotification: boolean = false) { - const response = await expect(this.errorNotification.filter({hasText: text})).toBeVisible({visible: isVisible}); - if (deleteNotification) { - await this.errorNotification.filter({hasText: text}).locator('svg').click(); - } - return response; + async selectMediaWithName(mediaName: string, isForce: boolean = false) { + const mediaLocator = this.mediaCardItems.filter({hasText: mediaName}); + await this.waitForVisible(mediaLocator); + await mediaLocator.click({position: {x: 0.5, y: 0.5}, force: isForce}); } - async isSectionWithNameVisible(sectionName: string, isVisible: boolean = true) { - await this.isVisible(this.page.getByRole('tab', {name: sectionName}), isVisible); + async selectMediaWithTestId(mediaKey: string) { + const locator = this.page.getByTestId('media:' + mediaKey); + await this.waitForVisible(locator); + await locator.click({position: {x: 0.5, y: 0.5}}); } - async clickMediaWithName(name: string) { - await this.click(this.mediaCardItems.filter({hasText: name})); + async clickMediaPickerModalSubmitButton() { + await this.click(this.mediaPickerModalSubmitBtn); } - async clickChooseContentStartNodeButton() { - await this.click(this.chooseDocumentInputBtn); + async clickMediaCaptionAltTextModalSubmitButton() { + await this.click(this.mediaCaptionAltTextModalSubmitBtn); } async clickChooseMediaStartNodeButton() { await this.click(this.chooseMediaInputBtn); } - async clickActionButton() { - await this.click(this.actionBtn); + async isMediaCardItemWithNameDisabled(itemName: string) { + await this.hasAttribute(this.mediaCardItems.filter({hasText: itemName}), 'class', 'not-allowed'); } - async clickReferenceNodeLinkWithName(name: string) { - await this.click(this.page.locator('[name="' + name + '"] a#open-part')); + async isMediaCardItemWithNameVisible(itemName: string, isVisible: boolean = true) { + await this.isVisible(this.mediaCardItems.filter({hasText: itemName}), isVisible); } - async clickLinkWithName(name: string, isExact: boolean = false) { - await this.click(this.page.getByRole('link', {name: name, exact: isExact})); + async doesMediaHaveThumbnail(mediaId: string, thumbnailIconName: string, thumbnailImage: string) { + const mediaThumbnailLocator = this.page.getByTestId('media:' + mediaId); + if (thumbnailIconName === 'image') { + const regexImageSrc = new RegExp(`^${thumbnailImage}.*`); + await this.hasAttribute(mediaThumbnailLocator.locator('umb-imaging-thumbnail img'), 'src', regexImageSrc.toString()); + } else { + await this.hasAttribute(mediaThumbnailLocator.locator('umb-imaging-thumbnail umb-icon'), 'name', thumbnailIconName); + } } - async clickMediaPickerModalSubmitButton() { - await this.click(this.mediaPickerModalSubmitBtn); + async isInputDropzoneVisible(isVisible: boolean = true) { + await this.isVisible(this.inputDropzone, isVisible); } - async selectMediaWithName(mediaName: string, isForce: boolean = false) { - const mediaLocator = this.mediaCardItems.filter({hasText: mediaName}); - await this.waitForVisible(mediaLocator); - await mediaLocator.click({position: {x: 0.5, y: 0.5}, force: isForce}); + async isImageCropperFieldVisible(isVisible: boolean = true) { + await this.isVisible(this.imageCropperField, isVisible); } - async selectMediaWithTestId(mediaKey: string) { - const locator = this.page.getByTestId('media:' + mediaKey); - await this.waitForVisible(locator); - await locator.click({position: {x: 0.5, y: 0.5}}); + async isInputUploadFieldVisible(isVisible: boolean = true) { + await this.isVisible(this.inputUploadField, isVisible); } + // #endregion - async clickCreateModalButton() { - await this.click(this.createModalBtn); + // #region Upload Methods + async clickToUploadButton() { + await this.click(this.clickToUploadBtn); } - async clickMediaCaptionAltTextModalSubmitButton() { - await this.click(this.mediaCaptionAltTextModalSubmitBtn); + async uploadFile(filePath: string) { + const [fileChooser] = await Promise.all([ + this.page.waitForEvent('filechooser'), + await this.clickToUploadButton(), + ]); + await fileChooser.setFiles(filePath); } + // #endregion - // Embed Modal + // #region Embedded Media Methods async enterEmbeddedURL(value: string) { await this.enterText(this.embeddedURLTxt, value); } @@ -1130,26 +1394,21 @@ export class UiBaseLocators extends BasePage { async waitForEmbeddedPreviewVisible() { await this.waitForVisible(this.embeddedPreview); } + // #endregion - async isSubmitButtonDisabled() { - await this.isVisible(this.submitBtn); - await this.isDisabled(this.submitBtn); - } - - async doesMediaHaveThumbnail(mediaId: string, thumbnailIconName: string, thumbnailImage: string) { - const mediaThumbnailLocator = this.page.getByTestId('media:' + mediaId); - if (thumbnailIconName === 'image') { - const regexImageSrc = new RegExp(`^${thumbnailImage}.*`); - await this.hasAttribute(mediaThumbnailLocator.locator('umb-imaging-thumbnail img'), 'src', regexImageSrc.toString()); - } else { - await this.hasAttribute(mediaThumbnailLocator.locator('umb-imaging-thumbnail umb-icon'), 'name', thumbnailIconName); - } + // #region Document Methods + async clickChooseContentStartNodeButton() { + await this.click(this.chooseDocumentInputBtn); } + // #endregion + // #region User Methods async clickCurrentUserAvatarButton() { await this.click(this.currentUserAvatarBtn, {force: true}); } + // #endregion + // #region Collection Methods async clickCreateActionButton() { await this.click(this.createActionBtn); } @@ -1169,9 +1428,11 @@ export class UiBaseLocators extends BasePage { await this.waitForVisible(this.collectionTreeItemTableRow.first()); await this.isVisible(this.collectionTreeItemTableRow.filter({hasText: name}).locator('umb-icon').locator('[name="' + icon + '"]')); } + // #endregion - async clickFolderButton() { - await this.click(this.folderBtn); + // #region Reference Methods + async clickReferenceNodeLinkWithName(name: string) { + await this.click(this.page.locator('[name="' + name + '"] a#open-part')); } async doesReferenceHeadlineHaveText(text: string) { @@ -1193,26 +1454,9 @@ export class UiBaseLocators extends BasePage { async doesReferencesContainText(text: string) { await this.containsText(this.confirmActionModalEntityReferences, text); } + // #endregion - async isValidationMessageVisible(message: string, isVisible: boolean = true) { - await this.isVisible(this.validationMessage.filter({hasText: message}), isVisible); - } - - async isSuccessStateIconVisible() { - await this.isVisible(this.successStateIcon); - } - - async isPropertyEditorUiWithNameReadOnly(name: string) { - const propertyEditorUiLocator = this.page.locator('umb-property-editor-ui-' + name); - await this.hasAttribute(propertyEditorUiLocator, 'readonly', ''); - } - - async isPropertyEditorUiWithNameVisible(name: string, isVisible: boolean = true) { - const propertyEditorUiLocator = this.page.locator('umb-property-editor-ui-' + name); - await this.isVisible(propertyEditorUiLocator, isVisible); - } - - // Entity Action + // #region Entity Action Methods async clickEntityActionWithName(name: string) { const regex = new RegExp(`^entity-action:.*${name}$`); await this.openEntityAction.getByTestId(regex).filter({has: this.page.locator(':visible')}).click(); @@ -1298,80 +1542,86 @@ export class UiBaseLocators extends BasePage { await this.clickEntityActionWithName('Update'); } - async clickModalMenuItemWithName(name: string) { - await this.click(this.openedModal.locator('uui-menu-item[label="' + name + '"]')); - } - - async isModalMenuItemWithNameDisabled(name: string) { - await this.hasAttribute(this.sidebarModal.locator('uui-menu-item[label="' + name + '"]'), 'disabled', ''); - } - - async doesPropertyHaveInvalidBadge(propertyName: string) { - await this.isVisible(this.page.locator('umb-property-layout').filter({hasText: propertyName}).locator('#invalid-badge uui-badge')); - } - - async isModalMenuItemWithNameVisible(name: string, isVisible: boolean = true) { - await this.isVisible(this.sidebarModal.locator('uui-menu-item[label="' + name + '"]'), isVisible); + async clickLockActionMenuOption() { + await this.clickEntityActionWithName('Lock'); } + // #endregion + // #region Entity Item Methods async clickEntityItemByName(itemName: string) { await this.click(this.page.locator('uui-ref-node,umb-ref-item[name="' + itemName + '"]')); } + // #endregion - async isMediaCardItemWithNameDisabled(itemName: string) { - await this.hasAttribute(this.mediaCardItems.filter({hasText: itemName}), 'class', 'not-allowed'); - } - - async isMediaCardItemWithNameVisible(itemName: string, isVisible: boolean = true) { - await this.isVisible(this.mediaCardItems.filter({hasText: itemName}), isVisible); - } - + // #region Workspace Action Methods async clickWorkspaceActionMenuButton() { await this.click(this.workspaceActionMenuBtn); } + // #endregion - async clickLockActionMenuOption() { - await this.clickEntityActionWithName('Lock'); - } - - async isDashboardTabWithNameVisible(name: string, isVisible: boolean = true) { - await this.isVisible(this.page.locator('uui-tab[label="' + name + '"]'), isVisible); + // #region Pagination Methods + async clickNextPaginationButton() { + await this.click(this.nextPaginationBtn); } + // #endregion + // #region Editor Methods async enterMonacoEditorValue(value: string) { await this.click(this.monacoEditor); await this.page.keyboard.press('Control+A'); await this.page.keyboard.press('Backspace'); await this.page.keyboard.insertText(value); } + // #endregion + // #region Loader Methods async waitUntilUiLoaderIsNoLongerVisible() { await this.waitForHidden(this.uiLoader, 10000); } + // #endregion + + // #region Dashboard Methods + async isDashboardTabWithNameVisible(name: string, isVisible: boolean = true) { + await this.isVisible(this.page.locator('uui-tab[label="' + name + '"]'), isVisible); + } async isWorkspaceViewTabWithAliasVisible(alias: string, isVisible: boolean = true) { await this.isVisible(this.page.getByTestId('workspace:view-link:' + alias), isVisible); } + // #endregion - async clickRestoreButton() { - await this.click(this.restoreBtn); + // #region Submit Button Methods + async isSubmitButtonDisabled() { + await this.isVisible(this.submitBtn); + await this.isDisabled(this.submitBtn); } + // #endregion - async isInputDropzoneVisible(isVisible: boolean = true) { - await this.isVisible(this.inputDropzone, isVisible); + // #region Data Element Methods + async clickDataElement(elementName: string, options: any = null) { + await this.page.click(`[data-element="${elementName}"]`, options); } - async isImageCropperFieldVisible(isVisible: boolean = true) { - await this.isVisible(this.imageCropperField, isVisible); + async getDataElement(elementName: string) { + return this.page.locator(`[data-element="${elementName}"]`); } - async isInputUploadFieldVisible(isVisible: boolean = true) { - await this.isVisible(this.inputUploadField, isVisible); + getLocatorWithDataMark(dataMark: string) { + return this.page.getByTestId(dataMark); } + // #endregion - async isBackOfficeMainVisible(isVisible: boolean = true) { - // We need to wait to make sure the page has loaded - await this.page.waitForTimeout(ConstantHelper.wait.medium); - await this.isVisible(this.backOfficeMain, isVisible); + // #region Text Visibility Methods + async isTextWithExactNameVisible(name: string, isVisible = true) { + return expect(this.page.getByText(name, {exact: true})).toBeVisible({visible: isVisible}); + } + + async isTextWithMessageVisible(message: string, isVisible: boolean = true) { + return await expect(this.page.getByText(message)).toBeVisible({visible: isVisible}); + } + + getTextLocatorWithName(name: string) { + return this.page.getByText(name, {exact: true}); } + // #endregion } From f2ea17ce45420c49c19dc1ff3afbc13e9fafa56f Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 14:14:31 +0700 Subject: [PATCH 06/34] Simplify section comments in UiBaseLocators.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace #region/#endregion blocks with simple separator comments - Format all comments as '// Comment here' with proper spacing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- lib/helpers/UiBaseLocators.ts | 391 ++++++++++++---------------------- 1 file changed, 141 insertions(+), 250 deletions(-) diff --git a/lib/helpers/UiBaseLocators.ts b/lib/helpers/UiBaseLocators.ts index c6697ec9..c135b508 100644 --- a/lib/helpers/UiBaseLocators.ts +++ b/lib/helpers/UiBaseLocators.ts @@ -1,9 +1,9 @@ -import {expect, Locator, Page} from "@playwright/test" +import {expect, Locator, Page} from "@playwright/test" import {ConstantHelper} from "./ConstantHelper"; import {BasePage} from "./BasePage"; export class UiBaseLocators extends BasePage { - // #region Core Action Buttons + // Core Action Buttons public readonly saveBtn: Locator; public readonly submitBtn: Locator; public readonly confirmBtn: Locator; @@ -25,9 +25,8 @@ export class UiBaseLocators extends BasePage { public readonly enableBtn: Locator; public readonly actionBtn: Locator; public readonly nextBtn: Locator; - // #endregion - // #region Confirmation Buttons + // Confirmation Buttons public readonly confirmToDeleteBtn: Locator; public readonly confirmCreateFolderBtn: Locator; public readonly confirmToRemoveBtn: Locator; @@ -36,9 +35,8 @@ export class UiBaseLocators extends BasePage { public readonly confirmEnableBtn: Locator; public readonly confirmRenameBtn: Locator; public readonly confirmTrashBtn: Locator; - // #endregion - // #region Folder Management + // Folder Management public readonly createFolderBtn: Locator; public readonly folderNameTxt: Locator; public readonly folderBtn: Locator; @@ -47,9 +45,8 @@ export class UiBaseLocators extends BasePage { public readonly renameFolderBtn: Locator; public readonly updateFolderBtn: Locator; public readonly deleteFolderThreeDotsBtn: Locator; - // #endregion - // #region Navigation & Menu + // Navigation & Menu public readonly breadcrumbBtn: Locator; public readonly leftArrowBtn: Locator; public readonly caretBtn: Locator; @@ -60,24 +57,21 @@ export class UiBaseLocators extends BasePage { public readonly sectionSidebar: Locator; public readonly menuItem: Locator; public readonly actionsMenuContainer: Locator; - // #endregion - // #region Three Dots Menu Buttons + // Three Dots Menu Buttons public readonly createThreeDotsBtn: Locator; public readonly renameThreeDotsBtn: Locator; public readonly deleteThreeDotsBtn: Locator; - // #endregion - // #region Modal & Container + // Modal & Container public readonly sidebarModal: Locator; public readonly openedModal: Locator; public readonly container: Locator; public readonly containerChooseBtn: Locator; public readonly containerSaveAndPublishBtn: Locator; public readonly createModalBtn: Locator; - // #endregion - // #region Document Type & Property Editor + // Document Type & Property Editor public readonly documentTypeNode: Locator; public readonly propertyNameTxt: Locator; public readonly selectPropertyEditorBtn: Locator; @@ -86,38 +80,33 @@ export class UiBaseLocators extends BasePage { public readonly property: Locator; public readonly addPropertyBtn: Locator; public readonly labelAboveBtn: Locator; - // #endregion - // #region Group & Tab Management + // Group & Tab Management public readonly addGroupBtn: Locator; public readonly groupLabel: Locator; public readonly typeGroups: Locator; public readonly addTabBtn: Locator; public readonly unnamedTabTxt: Locator; public readonly structureTabBtn: Locator; - // #endregion - // #region Validation & Mandatory + // Validation & Mandatory public readonly mandatoryToggle: Locator; public readonly validation: Locator; public readonly regexTxt: Locator; public readonly regexMessageTxt: Locator; public readonly validationMessage: Locator; - // #endregion - // #region Composition & Structure + // Composition & Structure public readonly compositionsBtn: Locator; public readonly allowAtRootBtn: Locator; public readonly allowedChildNodesModal: Locator; public readonly addCollectionBtn: Locator; - // #endregion - // #region Reorder + // Reorder public readonly iAmDoneReorderingBtn: Locator; public readonly reorderBtn: Locator; - // #endregion - // #region Query Builder + // Query Builder public readonly queryBuilderBtn: Locator; public readonly queryBuilderOrderedBy: Locator; public readonly queryBuilderCreateDate: Locator; @@ -130,9 +119,8 @@ export class UiBaseLocators extends BasePage { public readonly chooseRootContentBtn: Locator; public readonly returnedItemsCount: Locator; public readonly queryResults: Locator; - // #endregion - // #region Insert & Template + // Insert & Template public readonly insertValueBtn: Locator; public readonly insertPartialViewBtn: Locator; public readonly insertDictionaryItemBtn: Locator; @@ -140,56 +128,47 @@ export class UiBaseLocators extends BasePage { public readonly systemFieldsOption: Locator; public readonly chooseFieldValueDropDown: Locator; public readonly breadcrumbsTemplateModal: Locator; - // #endregion - // #region Rename + // Rename public readonly newNameTxt: Locator; public readonly renameModalBtn: Locator; - // #endregion - // #region State & Notification + // State & Notification public readonly successState: Locator; public readonly successStateIcon: Locator; public readonly failedStateButton: Locator; public readonly successNotification: Locator; public readonly errorNotification: Locator; - // #endregion - // #region Search & Filter + // Search & Filter public readonly typeToFilterSearchTxt: Locator; public readonly filterChooseBtn: Locator; - // #endregion - // #region Text Input + // Text Input public readonly textAreaInputArea: Locator; public readonly enterAName: Locator; public readonly descriptionBtn: Locator; public readonly enterDescriptionTxt: Locator; public readonly aliasLockBtn: Locator; public readonly aliasNameTxt: Locator; - // #endregion - // #region Icon + // Icon public readonly iconBtn: Locator; - // #endregion - // #region Create Link + // Create Link public readonly createLink: Locator; - // #endregion - // #region Recycle Bin + // Recycle Bin public readonly recycleBinBtn: Locator; public readonly recycleBinMenuItem: Locator; public readonly recycleBinMenuItemCaretBtn: Locator; - // #endregion - // #region View Options + // View Options public readonly gridBtn: Locator; public readonly listBtn: Locator; public readonly viewBundleBtn: Locator; - // #endregion - // #region Media + // Media public readonly mediaCardItems: Locator; public readonly mediaPickerModalSubmitBtn: Locator; public readonly mediaCaptionAltTextModalSubmitBtn: Locator; @@ -198,68 +177,58 @@ export class UiBaseLocators extends BasePage { public readonly imageCropperField: Locator; public readonly inputUploadField: Locator; public readonly chooseMediaInputBtn: Locator; - // #endregion - // #region Embedded Media + // Embedded Media public readonly embeddedMediaModal: Locator; public readonly embeddedURLTxt: Locator; public readonly embeddedRetrieveBtn: Locator; public readonly embeddedMediaModalConfirmBtn: Locator; public readonly embeddedPreview: Locator; - // #endregion - // #region Document & Content + // Document & Content public readonly chooseDocumentInputBtn: Locator; public readonly createDocumentBlueprintBtn: Locator; public readonly createDocumentBlueprintModal: Locator; public readonly createNewDocumentBlueprintBtn: Locator; - // #endregion - // #region User + // User public readonly currentUserAvatarBtn: Locator; public readonly newPasswordTxt: Locator; public readonly confirmPasswordTxt: Locator; public readonly currentPasswordTxt: Locator; - // #endregion - // #region Collection & Table + // Collection & Table public readonly collectionTreeItemTableRow: Locator; public readonly createActionButtonCollection: Locator; public readonly createActionBtn: Locator; public readonly createOptionActionListModal: Locator; - // #endregion - // #region Reference & Entity + // Reference & Entity public readonly confirmActionModalEntityReferences: Locator; public readonly referenceHeadline: Locator; public readonly entityItemRef: Locator; public readonly entityItem: Locator; - // #endregion - // #region Workspace & Action + // Workspace & Action public readonly workspaceAction: Locator; public readonly workspaceActionMenuBtn: Locator; public readonly entityAction: Locator; public readonly openEntityAction: Locator; - // #endregion - // #region Pagination + // Pagination public readonly firstPaginationBtn: Locator; public readonly nextPaginationBtn: Locator; - // #endregion - // #region Editor + // Editor public readonly monacoEditor: Locator; - // #endregion - // #region Loader + // Loader public readonly uiLoader: Locator; - // #endregion constructor(page: Page) { super(page); - // #region Core Action Buttons + // Core Action Buttons this.saveBtn = page.getByLabel('Save', {exact: true}); this.submitBtn = page.getByLabel('Submit'); this.confirmBtn = page.getByLabel('Confirm'); @@ -281,9 +250,8 @@ export class UiBaseLocators extends BasePage { this.enableBtn = page.getByLabel('Enable'); this.actionBtn = page.getByTestId('workspace:action-menu-button'); this.nextBtn = page.getByLabel('Next'); - // #endregion - - // #region Confirmation Buttons + + // Confirmation Buttons this.confirmToDeleteBtn = page.locator('#confirm').getByLabel('Delete'); this.confirmCreateFolderBtn = page.locator('#confirm').getByLabel('Create Folder'); this.confirmToRemoveBtn = page.locator('#confirm').getByLabel('Remove'); @@ -292,9 +260,8 @@ export class UiBaseLocators extends BasePage { this.confirmEnableBtn = page.locator('#confirm').getByLabel('Enable'); this.confirmRenameBtn = page.locator('#confirm').getByLabel('Rename'); this.confirmTrashBtn = page.locator('#confirm').getByLabel('Trash'); - // #endregion - - // #region Folder Management + + // Folder Management this.createFolderBtn = page.getByLabel('Create folder'); this.folderNameTxt = page.getByLabel('Enter a folder name'); this.folderBtn = page.locator('umb-entity-create-option-action-list-modal').locator('umb-ref-item', {hasText: 'Folder'}); @@ -303,9 +270,8 @@ export class UiBaseLocators extends BasePage { this.renameFolderBtn = page.getByLabel('Rename folder'); this.updateFolderBtn = page.getByLabel('Update folder'); this.deleteFolderThreeDotsBtn = page.locator('#action-modal').getByLabel('Delete Folder...'); - // #endregion - - // #region Navigation & Menu + + // Navigation & Menu this.breadcrumbBtn = page.getByLabel('Breadcrumb'); this.leftArrowBtn = page.locator('[name="icon-arrow-left"] svg'); this.caretBtn = page.locator('#caret-button'); @@ -316,24 +282,21 @@ export class UiBaseLocators extends BasePage { this.sectionSidebar = page.locator('umb-section-sidebar'); this.menuItem = page.locator('uui-menu-item'); this.actionsMenuContainer = page.locator('uui-scroll-container'); - // #endregion - - // #region Three Dots Menu Buttons + + // Three Dots Menu Buttons this.createThreeDotsBtn = page.getByText('Create…', {exact: true}); this.renameThreeDotsBtn = page.getByLabel('Rename…', {exact: true}); this.deleteThreeDotsBtn = page.getByLabel('Delete…'); - // #endregion - - // #region Modal & Container + + // Modal & Container this.sidebarModal = page.locator('uui-modal-sidebar'); this.openedModal = page.locator('uui-modal-container[backdrop]'); this.container = page.locator('#container'); this.containerChooseBtn = page.locator('#container').getByLabel('Choose'); this.containerSaveAndPublishBtn = page.locator('#container').getByLabel('Save and Publish'); this.createModalBtn = page.locator('uui-modal-sidebar').getByLabel('Create', {exact: true}); - // #endregion - - // #region Document Type & Property Editor + + // Document Type & Property Editor this.documentTypeNode = page.locator('uui-ref-node-document-type'); this.propertyNameTxt = page.getByTestId('input:entity-name').locator('#input').first(); this.selectPropertyEditorBtn = page.getByLabel('Select Property Editor'); @@ -342,38 +305,33 @@ export class UiBaseLocators extends BasePage { this.property = page.locator('umb-property'); this.addPropertyBtn = page.getByLabel('Add property', {exact: true}); this.labelAboveBtn = page.locator('.appearance-option').filter({hasText: 'Label above'}); - // #endregion - - // #region Group & Tab Management + + // Group & Tab Management this.addGroupBtn = page.getByLabel('Add group', {exact: true}); this.groupLabel = page.getByLabel('Group', {exact: true}); this.typeGroups = page.locator('umb-content-type-design-editor-group'); this.addTabBtn = page.getByLabel('Add tab'); this.unnamedTabTxt = page.getByTestId('tab:').getByTestId('tab:name-input').locator('#input'); this.structureTabBtn = page.locator('uui-tab').filter({hasText: 'Structure'}).locator('svg'); - // #endregion - - // #region Validation & Mandatory + + // Validation & Mandatory this.mandatoryToggle = page.locator('#mandatory #toggle'); this.validation = page.locator('#native'); this.regexTxt = page.locator('input[name="pattern"]'); this.regexMessageTxt = page.locator('textarea[name="pattern-message"]'); this.validationMessage = page.locator('umb-form-validation-message').locator('#messages'); - // #endregion - - // #region Composition & Structure + + // Composition & Structure this.compositionsBtn = page.getByLabel('Compositions'); this.allowAtRootBtn = page.locator('label').filter({hasText: 'Allow at root'}); this.allowedChildNodesModal = page.locator('umb-tree-picker-modal'); this.addCollectionBtn = page.locator('umb-input-content-type-collection-configuration #create-button'); - // #endregion - - // #region Reorder + + // Reorder this.iAmDoneReorderingBtn = page.getByLabel('I am done reordering'); this.reorderBtn = page.getByLabel('Reorder'); - // #endregion - - // #region Query Builder + + // Query Builder this.queryBuilderBtn = page.locator('#query-builder-button'); this.queryBuilderOrderedBy = page.locator('#property-alias-dropdown').getByLabel('Property alias'); this.queryBuilderCreateDate = page.locator('#property-alias-dropdown').getByText('CreateDate').locator(".."); @@ -386,9 +344,8 @@ export class UiBaseLocators extends BasePage { this.chooseRootContentBtn = page.getByLabel('Choose root document'); this.returnedItemsCount = page.locator('#results-count'); this.queryResults = page.locator('.query-results'); - // #endregion - - // #region Insert & Template + + // Insert & Template this.insertValueBtn = page.locator('uui-button').filter({has: page.locator('[key="template_insertPageField"]')}); this.insertPartialViewBtn = page.locator('uui-button').filter({has: page.locator('[key="template_insertPartialView"]')}); this.insertDictionaryItemBtn = page.locator('uui-button').filter({has: page.locator('[key="template_insertDictionaryItem"]')}); @@ -396,56 +353,47 @@ export class UiBaseLocators extends BasePage { this.systemFieldsOption = page.getByText('System fields'); this.chooseFieldValueDropDown = page.locator('#value #expand-symbol-wrapper'); this.breadcrumbsTemplateModal = page.locator('uui-modal-sidebar').locator('umb-template-workspace-editor uui-breadcrumbs'); - // #endregion - - // #region Rename + + // Rename this.newNameTxt = page.getByRole('textbox', {name: 'Enter new name...'}); this.renameModalBtn = page.locator('umb-rename-modal').getByLabel('Rename'); - // #endregion - - // #region State & Notification + + // State & Notification this.successState = page.locator('[state="success"]'); this.successStateIcon = page.locator('[state="success"]').locator('#state'); this.failedStateButton = page.locator('uui-button[state="failed"]'); this.successNotification = page.locator('uui-toast-notification[open][color="positive"]'); this.errorNotification = page.locator('uui-toast-notification[open][color="danger"]'); - // #endregion - - // #region Search & Filter + + // Search & Filter this.typeToFilterSearchTxt = page.locator('[type="search"] #input'); this.filterChooseBtn = page.locator('button').filter({hasText: 'Choose'}); - // #endregion - - // #region Text Input + + // Text Input this.textAreaInputArea = page.locator('textarea.ime-text-area'); this.enterAName = page.getByLabel('Enter a name...', {exact: true}); this.descriptionBtn = page.getByLabel('Description'); this.enterDescriptionTxt = page.getByLabel('Enter a description...'); this.aliasLockBtn = page.locator('#name').getByLabel('Unlock input'); this.aliasNameTxt = page.locator('#name').getByLabel('alias'); - // #endregion - - // #region Icon + + // Icon this.iconBtn = page.getByLabel('icon'); - // #endregion - - // #region Create Link + + // Create Link this.createLink = page.getByRole('link', {name: 'Create', exact: true}); - // #endregion - - // #region Recycle Bin + + // Recycle Bin this.recycleBinBtn = page.getByLabel('Recycle Bin', {exact: true}); this.recycleBinMenuItem = page.locator('uui-menu-item[label="Recycle Bin"]'); this.recycleBinMenuItemCaretBtn = page.locator('uui-menu-item[label="Recycle Bin"]').locator('#caret-button'); - // #endregion - - // #region View Options + + // View Options this.gridBtn = page.getByLabel('Grid'); this.listBtn = page.getByLabel('List'); this.viewBundleBtn = page.locator('umb-collection-view-bundle uui-button svg'); - // #endregion - - // #region Media + + // Media this.mediaCardItems = page.locator('uui-card-media'); this.mediaPickerModalSubmitBtn = page.locator('umb-media-picker-modal').getByLabel('Submit'); this.mediaCaptionAltTextModalSubmitBtn = page.locator('umb-media-caption-alt-text-modal').getByLabel('Submit'); @@ -454,66 +402,56 @@ export class UiBaseLocators extends BasePage { this.imageCropperField = page.locator('umb-image-cropper-field'); this.inputUploadField = page.locator('umb-input-upload-field').locator('#wrapperInner'); this.chooseMediaInputBtn = page.locator('umb-input-media').getByLabel('Choose'); - // #endregion - - // #region Embedded Media + + // Embedded Media this.embeddedMediaModal = page.locator('umb-embedded-media-modal'); this.embeddedURLTxt = page.locator('umb-embedded-media-modal').locator('[label="URL"] #input'); this.embeddedRetrieveBtn = page.locator('umb-embedded-media-modal').locator('[label="Retrieve"]'); this.embeddedMediaModalConfirmBtn = page.locator('umb-embedded-media-modal').getByLabel('Confirm'); this.embeddedPreview = page.locator('umb-embedded-media-modal').locator('[label="Preview"]'); - // #endregion - - // #region Document & Content + + // Document & Content this.chooseDocumentInputBtn = page.locator('umb-input-document').getByLabel('Choose'); this.createDocumentBlueprintBtn = page.getByLabel(/^Create Document Blueprint(…)?$/); this.createDocumentBlueprintModal = page.locator('umb-document-blueprint-options-create-modal'); this.createNewDocumentBlueprintBtn = page.locator('umb-document-blueprint-options-create-modal').locator('umb-ref-item', {hasText: 'Document Blueprint for'}); - // #endregion - - // #region User + + // User this.currentUserAvatarBtn = page.getByTestId('header-app:Umb.HeaderApp.CurrentUser').locator('uui-avatar'); this.currentPasswordTxt = page.locator('input[name="oldPassword"]'); this.newPasswordTxt = page.locator('input[name="newPassword"]'); this.confirmPasswordTxt = page.locator('input[name="confirmPassword"]'); - // #endregion - - // #region Collection & Table + + // Collection & Table this.collectionTreeItemTableRow = page.locator('umb-collection-workspace-view umb-table uui-table-row'); this.createActionButtonCollection = page.locator('umb-collection-create-action-button'); this.createActionBtn = page.locator('umb-collection-create-action-button').locator('[label="Create"]'); this.createOptionActionListModal = page.locator('umb-entity-create-option-action-list-modal'); - // #endregion - - // #region Reference & Entity + + // Reference & Entity this.confirmActionModalEntityReferences = page.locator('umb-confirm-action-modal-entity-references,umb-confirm-bulk-action-modal-entity-references'); this.referenceHeadline = page.locator('umb-confirm-action-modal-entity-references,umb-confirm-bulk-action-modal-entity-references').locator('#reference-headline').first(); this.entityItemRef = page.locator('umb-confirm-action-modal-entity-references,umb-confirm-bulk-action-modal-entity-references').locator('uui-ref-list').first().getByTestId('entity-item-ref'); this.entityItem = page.locator('umb-entity-item-ref'); - // #endregion - - // #region Workspace & Action + + // Workspace & Action this.workspaceAction = page.locator('umb-workspace-action'); this.workspaceActionMenuBtn = page.getByTestId('workspace:action-menu-button'); this.entityAction = page.locator('umb-entity-action-list umb-entity-action'); this.openEntityAction = page.locator('#action-modal[open]').locator(page.locator('umb-entity-action-list umb-entity-action')); - // #endregion - - // #region Pagination + + // Pagination this.firstPaginationBtn = page.locator('umb-collection-pagination').getByLabel('First'); this.nextPaginationBtn = page.locator('umb-collection-pagination').getByLabel('Next'); - // #endregion - - // #region Editor + + // Editor this.monacoEditor = page.locator('.monaco-editor'); - // #endregion - - // #region Loader + + // Loader this.uiLoader = page.locator('uui-loader'); - // #endregion - } + } - // #region Actions Menu Methods + // Actions Menu Methods async clickActionsMenuForNameInSectionSidebar(name: string) { await this.sectionSidebar.locator('[label="' + name + '"]').hover(); await this.sectionSidebar.locator('[label="' + name + '"] >> [label="Open actions menu"]').first().click(); @@ -530,9 +468,8 @@ export class UiBaseLocators extends BasePage { await this.page.locator('uui-menu-item[label="' + name + '"]').click(); await expect(this.page.locator('uui-menu-item[label="' + name + '"] #action-modal').first()).toBeVisible({visible: isVisible}); } - // #endregion - // #region Caret Button Methods + // Caret Button Methods async clickCaretButtonForName(name: string) { await this.isCaretButtonWithNameVisible(name); await this.page.locator('uui-menu-item[label="' + name + '"]').locator('#caret-button').first().click(); @@ -558,9 +495,8 @@ export class UiBaseLocators extends BasePage { await this.clickCaretButtonForName(name); } } - // #endregion - // #region Tree Methods + // Tree Methods async reloadTree(treeName: string) { await expect(this.page.getByLabel(treeName, {exact: true})).toBeVisible(); await this.page.waitForTimeout(ConstantHelper.wait.short); @@ -576,9 +512,8 @@ export class UiBaseLocators extends BasePage { async doesTreeItemHaveTheCorrectIcon(name: string, icon: string) { return await this.isVisible(this.page.locator('umb-tree-item').filter({hasText: name}).locator('umb-icon').locator('[name="' + icon + '"]')); } - // #endregion - // #region Core Button Click Methods + // Core Button Click Methods async clickReloadButton() { await this.click(this.reloadBtn); } @@ -671,9 +606,8 @@ export class UiBaseLocators extends BasePage { async clickLeftArrowButton() { await this.click(this.leftArrowBtn); } - // #endregion - // #region Confirmation Button Methods + // Confirmation Button Methods async clickConfirmToDeleteButton() { await this.click(this.confirmToDeleteBtn); await this.page.waitForTimeout(ConstantHelper.wait.short); @@ -713,9 +647,8 @@ export class UiBaseLocators extends BasePage { await this.clickDeleteActionMenuOption(); await this.clickConfirmToDeleteButton(); } - // #endregion - // #region Folder Methods + // Folder Methods async clickCreateFolderButton() { await this.click(this.createFolderBtn); await this.page.waitForTimeout(ConstantHelper.wait.short); @@ -756,9 +689,8 @@ export class UiBaseLocators extends BasePage { async clickUpdateFolderButton() { await this.click(this.updateFolderBtn); } - // #endregion - // #region Three Dots Menu Methods + // Three Dots Menu Methods async clickCreateThreeDotsButton() { await this.click(this.createThreeDotsBtn); } @@ -766,9 +698,8 @@ export class UiBaseLocators extends BasePage { async clickFilterChooseButton() { await this.click(this.filterChooseBtn); } - // #endregion - // #region Success State Methods + // Success State Methods async isSuccessStateVisibleForSaveButton(isVisible: boolean = true) { const regex = new RegExp(`^workspace-action:.*Save$`); const saveButtonLocator = this.page.getByTestId(regex); @@ -787,9 +718,8 @@ export class UiBaseLocators extends BasePage { async isFailedStateButtonVisible() { await this.isVisible(this.failedStateButton); } - // #endregion - // #region Notification Methods + // Notification Methods async isSuccessNotificationVisible(isVisible: boolean = true) { return await expect(this.successNotification.first()).toBeVisible({visible: isVisible, timeout: ConstantHelper.timeout.long}); } @@ -820,9 +750,8 @@ export class UiBaseLocators extends BasePage { } return response; } - // #endregion - // #region Modal Methods + // Modal Methods async clickChooseModalButton() { await this.click(this.chooseModalBtn); } @@ -842,16 +771,14 @@ export class UiBaseLocators extends BasePage { async isModalMenuItemWithNameVisible(name: string, isVisible: boolean = true) { await this.isVisible(this.sidebarModal.locator('uui-menu-item[label="' + name + '"]'), isVisible); } - // #endregion - // #region Container Methods + // Container Methods async clickContainerSaveAndPublishButton() { await this.click(this.containerSaveAndPublishBtn); await this.page.waitForTimeout(ConstantHelper.wait.short); } - // #endregion - // #region Navigation Methods + // Navigation Methods async goToSection(sectionName: string, checkSections = true, skipReload = false) { if (checkSections) { for (let section in ConstantHelper.sections) { @@ -879,9 +806,8 @@ export class UiBaseLocators extends BasePage { await this.page.waitForTimeout(ConstantHelper.wait.medium); await this.isVisible(this.backOfficeMain, isVisible); } - // #endregion - // #region Link & Button Click by Name Methods + // Link & Button Click by Name Methods async clickExactLinkWithName(name: string, toForce: boolean = false) { const exactLinkWithNameLocator = this.page.getByRole('link', {name: name, exact: true}); await this.click(exactLinkWithNameLocator, {force: toForce}); @@ -912,9 +838,8 @@ export class UiBaseLocators extends BasePage { await this.waitForVisible(this.page.getByRole('button', {name: name})); return this.page.getByRole('button', {name: name}); } - // #endregion - // #region Remove Button Methods + // Remove Button Methods async clickRemoveButtonForName(name: string) { const removeButtonWithNameLocator = this.page.locator('[name="' + name + '"] [label="Remove"]'); await this.click(removeButtonWithNameLocator); @@ -929,9 +854,8 @@ export class UiBaseLocators extends BasePage { const removeLabelWithNameLocator = this.page.locator('[label="Remove ' + name + '"]'); await this.click(removeLabelWithNameLocator); } - // #endregion - // #region Alias & Icon Methods + // Alias & Icon Methods async enterAliasName(aliasName: string) { await this.page.waitForTimeout(ConstantHelper.wait.short); await this.click(this.aliasLockBtn, {force: true}); @@ -944,9 +868,8 @@ export class UiBaseLocators extends BasePage { await this.clickLabelWithName(iconName, true, true); await this.clickSubmitButton(); } - // #endregion - // #region Property Editor Methods + // Property Editor Methods async clickSelectPropertyEditorButton() { await this.click(this.selectPropertyEditorBtn); } @@ -1007,9 +930,8 @@ export class UiBaseLocators extends BasePage { async doesPropertyHaveInvalidBadge(propertyName: string) { await this.isVisible(this.page.locator('umb-property-layout').filter({hasText: propertyName}).locator('#invalid-badge uui-badge')); } - // #endregion - // #region Group Methods + // Group Methods async clickAddGroupButton() { await this.click(this.addGroupBtn); } @@ -1050,9 +972,8 @@ export class UiBaseLocators extends BasePage { await this.dragAndDrop(dragFromLocator, dragToLocator, 0, 0, 20); return {firstGroupValue, secondGroupValue}; } - // #endregion - // #region Tab Methods + // Tab Methods async clickAddTabButton() { await this.click(this.addTabBtn); } @@ -1080,9 +1001,8 @@ export class UiBaseLocators extends BasePage { getTabLocatorWithName(name: string) { return this.page.getByRole('tab', {name: name}); } - // #endregion - // #region Validation Methods + // Validation Methods async clickMandatoryToggle() { await this.click(this.mandatoryToggle); } @@ -1102,9 +1022,8 @@ export class UiBaseLocators extends BasePage { async isValidationMessageVisible(message: string, isVisible: boolean = true) { await this.isVisible(this.validationMessage.filter({hasText: message}), isVisible); } - // #endregion - // #region Composition & Structure Methods + // Composition & Structure Methods async clickCompositionsButton() { await this.click(this.compositionsBtn); } @@ -1120,9 +1039,8 @@ export class UiBaseLocators extends BasePage { async clickAddCollectionButton() { await this.click(this.addCollectionBtn); } - // #endregion - // #region Reorder Methods + // Reorder Methods async clickIAmDoneReorderingButton() { await this.click(this.iAmDoneReorderingBtn); } @@ -1134,9 +1052,8 @@ export class UiBaseLocators extends BasePage { async clickLabelAboveButton() { await this.click(this.labelAboveBtn); } - // #endregion - // #region Query Builder Methods + // Query Builder Methods async clickQueryBuilderButton() { await this.click(this.queryBuilderBtn); await this.page.waitForTimeout(ConstantHelper.wait.short); @@ -1185,9 +1102,8 @@ export class UiBaseLocators extends BasePage { async doesQueryResultHaveContentName(contentName: string) { await this.containsText(this.queryResults, contentName); } - // #endregion - // #region Insert Methods + // Insert Methods async insertDictionaryItem(dictionaryName: string) { await this.clickInsertButton(); await this.click(this.insertDictionaryItemBtn); @@ -1211,9 +1127,8 @@ export class UiBaseLocators extends BasePage { await this.click(this.page.getByLabel(partialViewName)); await this.clickChooseButton(); } - // #endregion - // #region Rename Methods + // Rename Methods async rename(newName: string) { await this.clickRenameActionMenuOption(); await this.click(this.newNameTxt); @@ -1221,15 +1136,13 @@ export class UiBaseLocators extends BasePage { await this.click(this.renameModalBtn); await this.page.waitForTimeout(ConstantHelper.wait.short); } - // #endregion - // #region Search & Filter Methods + // Search & Filter Methods async searchForTypeToFilterValue(searchValue: string) { await this.enterText(this.typeToFilterSearchTxt, searchValue, {clearFirst: false}); } - // #endregion - // #region Description Methods + // Description Methods async enterDescription(description: string) { await this.enterText(this.enterDescriptionTxt, description); } @@ -1237,9 +1150,8 @@ export class UiBaseLocators extends BasePage { async doesDescriptionHaveValue(value: string, index: number = 0) { return await this.hasValue(this.descriptionBtn.nth(index), value); } - // #endregion - // #region Drag and Drop Methods + // Drag and Drop Methods async dragAndDrop(dragFromSelector: Locator, dragToSelector: Locator, verticalOffset: number = 0, horizontalOffset: number = 0, steps: number = 5) { await this.waitForVisible(dragFromSelector); await this.waitForVisible(dragToSelector); @@ -1255,15 +1167,13 @@ export class UiBaseLocators extends BasePage { await this.page.waitForTimeout(ConstantHelper.wait.debounce); await this.page.mouse.up(); } - // #endregion - // #region Create Link Methods + // Create Link Methods async clickCreateLink() { await this.click(this.createLink); } - // #endregion - // #region Recycle Bin Methods + // Recycle Bin Methods async clickRecycleBinButton() { await this.click(this.recycleBinBtn); } @@ -1286,9 +1196,8 @@ export class UiBaseLocators extends BasePage { } return await this.isVisible(this.page.locator('[label="Recycle Bin"] [label="' + item + '"]'), isVisible); } - // #endregion - // #region View Methods + // View Methods async changeToGridView() { await this.click(this.viewBundleBtn); await this.click(this.gridBtn); @@ -1302,9 +1211,8 @@ export class UiBaseLocators extends BasePage { async isViewBundleButtonVisible(isVisible: boolean = true) { return this.isVisible(this.viewBundleBtn, isVisible); } - // #endregion - // #region Media Methods + // Media Methods async clickMediaWithName(name: string) { await this.click(this.mediaCardItems.filter({hasText: name})); } @@ -1362,9 +1270,8 @@ export class UiBaseLocators extends BasePage { async isInputUploadFieldVisible(isVisible: boolean = true) { await this.isVisible(this.inputUploadField, isVisible); } - // #endregion - // #region Upload Methods + // Upload Methods async clickToUploadButton() { await this.click(this.clickToUploadBtn); } @@ -1376,9 +1283,8 @@ export class UiBaseLocators extends BasePage { ]); await fileChooser.setFiles(filePath); } - // #endregion - // #region Embedded Media Methods + // Embedded Media Methods async enterEmbeddedURL(value: string) { await this.enterText(this.embeddedURLTxt, value); } @@ -1394,21 +1300,18 @@ export class UiBaseLocators extends BasePage { async waitForEmbeddedPreviewVisible() { await this.waitForVisible(this.embeddedPreview); } - // #endregion - // #region Document Methods + // Document Methods async clickChooseContentStartNodeButton() { await this.click(this.chooseDocumentInputBtn); } - // #endregion - // #region User Methods + // User Methods async clickCurrentUserAvatarButton() { await this.click(this.currentUserAvatarBtn, {force: true}); } - // #endregion - // #region Collection Methods + // Collection Methods async clickCreateActionButton() { await this.click(this.createActionBtn); } @@ -1428,9 +1331,8 @@ export class UiBaseLocators extends BasePage { await this.waitForVisible(this.collectionTreeItemTableRow.first()); await this.isVisible(this.collectionTreeItemTableRow.filter({hasText: name}).locator('umb-icon').locator('[name="' + icon + '"]')); } - // #endregion - // #region Reference Methods + // Reference Methods async clickReferenceNodeLinkWithName(name: string) { await this.click(this.page.locator('[name="' + name + '"] a#open-part')); } @@ -1454,9 +1356,8 @@ export class UiBaseLocators extends BasePage { async doesReferencesContainText(text: string) { await this.containsText(this.confirmActionModalEntityReferences, text); } - // #endregion - // #region Entity Action Methods + // Entity Action Methods async clickEntityActionWithName(name: string) { const regex = new RegExp(`^entity-action:.*${name}$`); await this.openEntityAction.getByTestId(regex).filter({has: this.page.locator(':visible')}).click(); @@ -1545,42 +1446,36 @@ export class UiBaseLocators extends BasePage { async clickLockActionMenuOption() { await this.clickEntityActionWithName('Lock'); } - // #endregion - // #region Entity Item Methods + // Entity Item Methods async clickEntityItemByName(itemName: string) { await this.click(this.page.locator('uui-ref-node,umb-ref-item[name="' + itemName + '"]')); } - // #endregion - // #region Workspace Action Methods + // Workspace Action Methods async clickWorkspaceActionMenuButton() { await this.click(this.workspaceActionMenuBtn); } - // #endregion - // #region Pagination Methods + // Pagination Methods async clickNextPaginationButton() { await this.click(this.nextPaginationBtn); } - // #endregion - // #region Editor Methods + // Editor Methods async enterMonacoEditorValue(value: string) { await this.click(this.monacoEditor); await this.page.keyboard.press('Control+A'); await this.page.keyboard.press('Backspace'); await this.page.keyboard.insertText(value); } - // #endregion - // #region Loader Methods + // Loader Methods async waitUntilUiLoaderIsNoLongerVisible() { await this.waitForHidden(this.uiLoader, 10000); } - // #endregion - // #region Dashboard Methods + // Dashboard Methods async isDashboardTabWithNameVisible(name: string, isVisible: boolean = true) { await this.isVisible(this.page.locator('uui-tab[label="' + name + '"]'), isVisible); } @@ -1588,16 +1483,14 @@ export class UiBaseLocators extends BasePage { async isWorkspaceViewTabWithAliasVisible(alias: string, isVisible: boolean = true) { await this.isVisible(this.page.getByTestId('workspace:view-link:' + alias), isVisible); } - // #endregion - // #region Submit Button Methods + // Submit Button Methods async isSubmitButtonDisabled() { await this.isVisible(this.submitBtn); await this.isDisabled(this.submitBtn); } - // #endregion - // #region Data Element Methods + // Data Element Methods async clickDataElement(elementName: string, options: any = null) { await this.page.click(`[data-element="${elementName}"]`, options); } @@ -1609,9 +1502,8 @@ export class UiBaseLocators extends BasePage { getLocatorWithDataMark(dataMark: string) { return this.page.getByTestId(dataMark); } - // #endregion - // #region Text Visibility Methods + // Text Visibility Methods async isTextWithExactNameVisible(name: string, isVisible = true) { return expect(this.page.getByText(name, {exact: true})).toBeVisible({visible: isVisible}); } @@ -1623,5 +1515,4 @@ export class UiBaseLocators extends BasePage { getTextLocatorWithName(name: string) { return this.page.getByText(name, {exact: true}); } - // #endregion } From 6cdcb9eca742a41e038fbd36af55e9c46612bed6 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 14:44:58 +0700 Subject: [PATCH 07/34] Refactor UiBaseLocators to use BasePage.click and reduce code duplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace Playwright .click() with this.click() from BasePage - Add treeItem locator for tree item elements - Add getMenuItemByLabel() helper method to avoid repeated locator pattern - Use template literals for cleaner string interpolation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- lib/helpers/UiBaseLocators.ts | 64 ++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/lib/helpers/UiBaseLocators.ts b/lib/helpers/UiBaseLocators.ts index c135b508..f38a91bc 100644 --- a/lib/helpers/UiBaseLocators.ts +++ b/lib/helpers/UiBaseLocators.ts @@ -57,6 +57,7 @@ export class UiBaseLocators extends BasePage { public readonly sectionSidebar: Locator; public readonly menuItem: Locator; public readonly actionsMenuContainer: Locator; + public readonly treeItem: Locator; // Three Dots Menu Buttons public readonly createThreeDotsBtn: Locator; @@ -282,7 +283,8 @@ export class UiBaseLocators extends BasePage { this.sectionSidebar = page.locator('umb-section-sidebar'); this.menuItem = page.locator('uui-menu-item'); this.actionsMenuContainer = page.locator('uui-scroll-container'); - + this.treeItem = page.locator('umb-tree-item'); + // Three Dots Menu Buttons this.createThreeDotsBtn = page.getByText('Create…', {exact: true}); this.renameThreeDotsBtn = page.getByLabel('Rename…', {exact: true}); @@ -451,44 +453,50 @@ export class UiBaseLocators extends BasePage { this.uiLoader = page.locator('uui-loader'); } + // Helper Methods + getMenuItemByLabel(name: string): Locator { + return this.page.locator(`uui-menu-item[label="${name}"]`); + } + // Actions Menu Methods async clickActionsMenuForNameInSectionSidebar(name: string) { await this.sectionSidebar.locator('[label="' + name + '"]').hover(); - await this.sectionSidebar.locator('[label="' + name + '"] >> [label="Open actions menu"]').first().click(); + await this.click(this.sectionSidebar.locator('[label="' + name + '"] >> [label="Open actions menu"]').first()); } async clickActionsMenuForName(name: string) { - await expect(this.page.locator('uui-menu-item[label="' + name + '"]').locator('#menu-item').first()).toBeVisible(); + const menuItem = this.getMenuItemByLabel(name); await this.page.waitForTimeout(ConstantHelper.wait.medium); - await this.page.locator('uui-menu-item[label="' + name + '"]').locator('#menu-item').first().hover({force: true}); - await this.page.locator('uui-menu-item[label="' + name + '"] #action-modal').first().click({force: true}); + await menuItem.locator('#menu-item').first().hover({force: true}); + await this.click(menuItem.locator('#action-modal').first(), {force: true}); } async isActionsMenuForNameVisible(name: string, isVisible = true) { - await this.page.locator('uui-menu-item[label="' + name + '"]').click(); - await expect(this.page.locator('uui-menu-item[label="' + name + '"] #action-modal').first()).toBeVisible({visible: isVisible}); + const menuItem = this.getMenuItemByLabel(name); + await this.click(menuItem); + await expect(menuItem.locator('#action-modal').first()).toBeVisible({visible: isVisible}); } // Caret Button Methods async clickCaretButtonForName(name: string) { await this.isCaretButtonWithNameVisible(name); - await this.page.locator('uui-menu-item[label="' + name + '"]').locator('#caret-button').first().click(); + await this.click(this.getMenuItemByLabel(name).locator('#caret-button').first()); } async isCaretButtonWithNameVisible(name: string, isVisible = true) { - await expect(this.page.locator('uui-menu-item[label="' + name + '"]').locator('#caret-button').first()).toBeVisible({visible: isVisible}); + await expect(this.getMenuItemByLabel(name).locator('#caret-button').first()).toBeVisible({visible: isVisible}); } async clickCaretButton() { - await this.page.locator('#caret-button').click(); + await this.click(this.caretBtn); } async openCaretButtonForName(name: string, isInModal: boolean = false) { let menuItem: Locator; if (isInModal) { - menuItem = this.sidebarModal.locator('uui-menu-item[label="' + name + '"]'); + menuItem = this.sidebarModal.locator(`uui-menu-item[label="${name}"]`); } else { - menuItem = this.page.locator('uui-menu-item[label="' + name + '"]'); + menuItem = this.getMenuItemByLabel(name); } const isCaretButtonOpen = await menuItem.getAttribute('show-children'); if (isCaretButtonOpen === null) { @@ -498,7 +506,7 @@ export class UiBaseLocators extends BasePage { // Tree Methods async reloadTree(treeName: string) { - await expect(this.page.getByLabel(treeName, {exact: true})).toBeVisible(); + await this.isVisible(this.page.getByLabel(treeName, {exact: true})); await this.page.waitForTimeout(ConstantHelper.wait.short); await this.clickActionsMenuForName(treeName); await this.clickReloadChildrenActionMenuOption(); @@ -506,11 +514,11 @@ export class UiBaseLocators extends BasePage { } async isTreeItemVisible(name: string, isVisible = true) { - await this.isVisible(this.page.locator('umb-tree-item').locator('[label="' + name + '"]'), isVisible); + await this.isVisible(this.treeItem.locator('[label="' + name + '"]'), isVisible); } async doesTreeItemHaveTheCorrectIcon(name: string, icon: string) { - return await this.isVisible(this.page.locator('umb-tree-item').filter({hasText: name}).locator('umb-icon').locator('[name="' + icon + '"]')); + return await this.isVisible(this.treeItem.filter({hasText: name}).locator('umb-icon').locator('[name="' + icon + '"]')); } // Core Button Click Methods @@ -734,7 +742,7 @@ export class UiBaseLocators extends BasePage { timeout: timeout }); if (deleteNotification) { - await this.successNotification.filter({hasText: text}).getByLabel('close').click({force: true}); + await this.click(this.successNotification.filter({hasText: text}).getByLabel('close'), {force: true}); } return response; } @@ -746,7 +754,7 @@ export class UiBaseLocators extends BasePage { async doesErrorNotificationHaveText(text: string, isVisible: boolean = true, deleteNotification: boolean = false) { const response = await expect(this.errorNotification.filter({hasText: text})).toBeVisible({visible: isVisible}); if (deleteNotification) { - await this.errorNotification.filter({hasText: text}).locator('svg').click(); + await this.click(this.errorNotification.filter({hasText: text}).locator('svg')); } return response; } @@ -761,15 +769,15 @@ export class UiBaseLocators extends BasePage { } async clickModalMenuItemWithName(name: string) { - await this.click(this.openedModal.locator('uui-menu-item[label="' + name + '"]')); + await this.click(this.openedModal.locator(`uui-menu-item[label="${name}"]`)); } async isModalMenuItemWithNameDisabled(name: string) { - await this.hasAttribute(this.sidebarModal.locator('uui-menu-item[label="' + name + '"]'), 'disabled', ''); + await this.hasAttribute(this.sidebarModal.locator(`uui-menu-item[label="${name}"]`), 'disabled', ''); } async isModalMenuItemWithNameVisible(name: string, isVisible: boolean = true) { - await this.isVisible(this.sidebarModal.locator('uui-menu-item[label="' + name + '"]'), isVisible); + await this.isVisible(this.sidebarModal.locator(`uui-menu-item[label="${name}"]`), isVisible); } // Container Methods @@ -789,7 +797,7 @@ export class UiBaseLocators extends BasePage { if (alreadySelected && !skipReload) { await this.page.reload(); } else { - await this.backOfficeHeader.getByRole('tab', {name: sectionName}).click(); + await this.click(this.backOfficeHeader.getByRole('tab', {name: sectionName})); } } @@ -903,7 +911,7 @@ export class UiBaseLocators extends BasePage { async deletePropertyEditor(propertyEditorName: string) { await this.page.locator('uui-button').filter({hasText: propertyEditorName}).getByLabel('Editor settings').hover(); - await this.deleteBtn.click(); + await this.click(this.deleteBtn); } async deletePropertyEditorWithName(name: string) { @@ -982,8 +990,8 @@ export class UiBaseLocators extends BasePage { await this.waitForVisible(this.unnamedTabTxt); await this.page.waitForTimeout(ConstantHelper.wait.debounce); await this.enterText(this.unnamedTabTxt, tabName); - await this.page.getByRole('tab', {name: 'Design'}).click(); - await this.page.getByTestId('tab:' + tabName).click(); + await this.click(this.page.getByRole('tab', {name: 'Design'})); + await this.click(this.page.getByTestId('tab:' + tabName)); } async clickRemoveTabWithName(name: string) { @@ -1092,7 +1100,7 @@ export class UiBaseLocators extends BasePage { async isQueryBuilderCodeShown(code: string) { await this.click(this.queryBuilderShowCode); - await this.containsText(this.queryBuilderShowCode, code, 10000); + await this.containsText(this.queryBuilderShowCode, code, ConstantHelper.timeout.long); } async doesReturnedItemsHaveCount(itemCount: number) { @@ -1220,12 +1228,14 @@ export class UiBaseLocators extends BasePage { async selectMediaWithName(mediaName: string, isForce: boolean = false) { const mediaLocator = this.mediaCardItems.filter({hasText: mediaName}); await this.waitForVisible(mediaLocator); + // Using direct click with position option (not supported by BasePage.click) await mediaLocator.click({position: {x: 0.5, y: 0.5}, force: isForce}); } async selectMediaWithTestId(mediaKey: string) { const locator = this.page.getByTestId('media:' + mediaKey); await this.waitForVisible(locator); + // Using direct click with position option (not supported by BasePage.click) await locator.click({position: {x: 0.5, y: 0.5}}); } @@ -1360,7 +1370,7 @@ export class UiBaseLocators extends BasePage { // Entity Action Methods async clickEntityActionWithName(name: string) { const regex = new RegExp(`^entity-action:.*${name}$`); - await this.openEntityAction.getByTestId(regex).filter({has: this.page.locator(':visible')}).click(); + await this.click(this.openEntityAction.getByTestId(regex).filter({has: this.page.locator(':visible')})); } async clickCreateActionMenuOption() { @@ -1492,7 +1502,7 @@ export class UiBaseLocators extends BasePage { // Data Element Methods async clickDataElement(elementName: string, options: any = null) { - await this.page.click(`[data-element="${elementName}"]`, options); + await this.click(this.page.locator(`[data-element="${elementName}"]`), options); } async getDataElement(elementName: string) { From 1cb1441f3d3402ac4eeb9922c624a67de7bf0914 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 15:50:15 +0700 Subject: [PATCH 08/34] Refactor UI helpers to use BasePage methods and time constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace direct Playwright calls with BasePage wrapper methods (click, enterText, hover) across all UI helpers for consistency and maintainability. Also standardizes hardcoded timeout values to use ConstantHelper constants. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- lib/helpers/ContentUiHelper.ts | 565 +++++++++------------- lib/helpers/CurrentUserProfileUiHelper.ts | 13 +- lib/helpers/DataTypeUiHelper.ts | 147 +++--- lib/helpers/DictionaryUiHelper.ts | 23 +- lib/helpers/DocumentBlueprintUiHelper.ts | 13 +- lib/helpers/DocumentTypeUiHelper.ts | 45 +- lib/helpers/LanguageUiHelper.ts | 30 +- lib/helpers/LoginUiHelper.ts | 15 +- lib/helpers/MediaTypeUiHelper.ts | 23 +- lib/helpers/MediaUiHelper.ts | 55 +-- lib/helpers/MemberGroupUiHelper.ts | 21 +- lib/helpers/MemberTypeUiHelper.ts | 5 +- lib/helpers/MemberUiHelper.ts | 70 +-- lib/helpers/PackageUiHelper.ts | 59 ++- lib/helpers/PartialViewUiHelper.ts | 18 +- lib/helpers/RelationTypeUiHelper.ts | 14 +- lib/helpers/ScriptUiHelper.ts | 13 +- lib/helpers/StylesheetUiHelper.ts | 18 +- lib/helpers/TemplateUiHelper.ts | 29 +- lib/helpers/UserGroupUiHelper.ts | 59 +-- lib/helpers/UserUiHelper.ts | 79 ++- lib/helpers/WebhookUiHelper.ts | 37 +- 22 files changed, 549 insertions(+), 802 deletions(-) diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index 1719b8ca..ee4bd060 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -377,37 +377,35 @@ export class ContentUiHelper extends UiBaseLocators { } async clickSaveAndPublishButton() { - await expect(this.saveAndPublishBtn).toBeVisible(); - await this.saveAndPublishBtn.click(); - await this.page.waitForTimeout(500); + await this.click(this.saveAndPublishBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async isSuccessStateVisibleForSaveAndPublishButton (isVisible: boolean = true){ const saveAndPublishBtn = this.workspaceAction.filter({has: this.saveAndPublishBtn}); - await expect(saveAndPublishBtn.locator(this.successState)).toBeVisible({visible: isVisible, timeout: 10000}); + await expect(saveAndPublishBtn.locator(this.successState)).toBeVisible({visible: isVisible, timeout: ConstantHelper.timeout.long}); } - + async clickPublishButton() { - await this.publishBtn.click(); - await this.page.waitForTimeout(500); + await this.click(this.publishBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickUnpublishButton() { - await this.unpublishBtn.click(); + await this.click(this.unpublishBtn); } async clickReloadChildrenThreeDotsButton() { - await this.reloadChildrenThreeDotsBtn.click(); + await this.click(this.reloadChildrenThreeDotsBtn); } async clickActionsMenuAtRoot() { - await this.actionMenuForContentBtn.click({force: true}); + await this.click(this.actionMenuForContentBtn, {force: true}); } async goToContentWithName(contentName: string) { const contentWithNameLocator = this.menuItemTree.getByText(contentName, {exact: true}); - await expect(contentWithNameLocator).toBeVisible(); - await contentWithNameLocator.click(); + await this.click(contentWithNameLocator); } async clickActionsMenuForContent(name: string) { @@ -424,8 +422,7 @@ export class ContentUiHelper extends UiBaseLocators { } async clickCaretButtonForContentName(name: string) { - await expect(this.menuItemTree.filter({hasText: name}).last().locator('#caret-button').last()).toBeVisible(); - await this.menuItemTree.filter({hasText: name}).last().locator('#caret-button').last().click(); + await this.click(this.menuItemTree.filter({hasText: name}).last().locator('#caret-button').last()); } async waitForModalVisible() { @@ -437,9 +434,8 @@ export class ContentUiHelper extends UiBaseLocators { } async clickSaveButtonForContent() { - await expect(this.saveContentBtn).toBeVisible(); - await this.saveContentBtn.click(); - await this.page.waitForTimeout(500); + await this.click(this.saveContentBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async enterTextstring(text: string) { @@ -465,17 +461,16 @@ export class ContentUiHelper extends UiBaseLocators { } async clickConfirmToUnpublishButton() { - await this.confirmToUnpublishBtn.click(); + await this.click(this.confirmToUnpublishBtn); } async clickCreateDocumentBlueprintButton() { - await this.createDocumentBlueprintBtn.click(); + await this.click(this.createDocumentBlueprintBtn); } // Info Tab async clickInfoTab() { - await expect(this.infoTab).toBeVisible(); - await this.infoTab.click(); + await this.click(this.infoTab); } async doesDocumentHaveLink(link: string) { @@ -499,11 +494,11 @@ export class ContentUiHelper extends UiBaseLocators { } async clickEditDocumentTypeButton() { - await this.editDocumentTypeBtn.click(); + await this.click(this.editDocumentTypeBtn); } async clickAddTemplateButton() { - await this.addTemplateBtn.click(); + await this.click(this.addTemplateBtn); } async waitForContentToBeCreated() { @@ -535,11 +530,11 @@ export class ContentUiHelper extends UiBaseLocators { } async clickDocumentTypeByName(documentTypeName: string) { - await this.page.locator('uui-ref-node-document-type[name="' + documentTypeName + '"]').click(); + await this.click(this.page.locator(`uui-ref-node-document-type[name="${documentTypeName}"]`)); } async clickTemplateByName(templateName: string) { - await this.page.locator('uui-ref-node[name="' + templateName + '"]').click(); + await this.click(this.page.locator(`uui-ref-node[name="${templateName}"]`)); } async isDocumentTypeModalVisible(documentTypeName: string) { @@ -551,12 +546,12 @@ export class ContentUiHelper extends UiBaseLocators { } async clickEditTemplateByName(templateName: string) { - await this.page.locator('uui-ref-node[name="' + templateName + '"]').getByLabel('Choose').click(); + await this.click(this.page.locator(`uui-ref-node[name="${templateName}"]`).getByLabel('Choose')); } async changeTemplate(oldTemplate: string, newTemplate: string) { await this.clickEditTemplateByName(oldTemplate); - await this.sidebarModal.getByLabel(newTemplate).click(); + await this.click(this.sidebarModal.getByLabel(newTemplate)); await this.clickChooseModalButton(); } @@ -567,61 +562,55 @@ export class ContentUiHelper extends UiBaseLocators { // Culture and Hostnames async clickCultureAndHostnamesButton() { - await this.cultureAndHostnamesBtn.click(); + await this.click(this.cultureAndHostnamesBtn); } - + async clickAddNewHostnameButton(){ - await expect(this.addNewHostnameBtn).toBeVisible(); - await this.addNewHostnameBtn.click(); + await this.click(this.addNewHostnameBtn); } async selectCultureLanguageOption(option: string) { - await expect(this.cultureLanguageDropdownBox).toBeVisible(); - await this.cultureLanguageDropdownBox.click(); - await expect(this.hostNameItem.getByText(option, {exact: true})).toBeVisible(); - await this.hostNameItem.getByText(option, {exact: true}).click(); + await this.click(this.cultureLanguageDropdownBox); + await this.click(this.hostNameItem.getByText(option, {exact: true})); } async selectHostnameLanguageOption(option: string, index: number = 0) { - await this.hostnameLanguageDropdownBox.nth(index).click(); - await this.hostnameComboBox.getByText(option).nth(index).click(); + await this.click(this.hostnameLanguageDropdownBox.nth(index)); + await this.click(this.hostnameComboBox.getByText(option).nth(index)); } async enterDomain(value: string, index: number = 0) { - await expect(this.hostnameTxt.nth(index)).toBeVisible(); - await this.hostnameTxt.nth(index).clear(); - await this.hostnameTxt.nth(index).fill(value); - await expect(this.hostnameTxt.nth(index)).toHaveValue(value); + await this.enterText(this.hostnameTxt.nth(index), value, {verify: true}); } async clickDeleteHostnameButton() { - await this.deleteHostnameBtn.first().click(); + await this.click(this.deleteHostnameBtn.first()); } async clickSaveModalButton() { - await this.saveModalBtn.click(); - await this.page.waitForTimeout(500); + await this.click(this.saveModalBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async chooseDocumentType(documentTypeName: string) { - await this.documentTypeNode.filter({hasText: documentTypeName}).click(); + await this.click(this.documentTypeNode.filter({hasText: documentTypeName})); } // Approved Color async clickApprovedColorByValue(value: string) { - await this.page.locator('uui-color-swatch[value="#' + value + '"] #swatch').click(); + await this.click(this.page.locator(`uui-color-swatch[value="#${value}"] #swatch`)); } // Checkbox list async chooseCheckboxListOption(optionValue: string) { - await this.page.locator('uui-checkbox[value="' + optionValue + '"] svg').click(); + await this.click(this.page.locator(`uui-checkbox[value="${optionValue}"] svg`)); } // Content Picker async addContentPicker(contentName: string) { await this.clickChooseButton(); - await this.sidebarModal.getByText(contentName).click(); - await this.chooseModalBtn.click(); + await this.click(this.sidebarModal.getByText(contentName)); + await this.click(this.chooseModalBtn); } async isOpenButtonVisibleInContentPicker(contentPickerName: string, isVisible: boolean = true) { @@ -629,7 +618,7 @@ export class ContentUiHelper extends UiBaseLocators { } async clickContentPickerOpenButton(contentPickerName: string) { - await this.page.getByLabel('Open ' + contentPickerName).click(); + await this.click(this.page.getByLabel('Open ' + contentPickerName)); } async isNodeOpenForContentPicker(contentPickerName: string) { @@ -649,9 +638,9 @@ export class ContentUiHelper extends UiBaseLocators { } async removeContentPicker(contentPickerName: string) { - const contentPickerLocator = this.entityItem.filter({has: this.page.locator('[name="' + contentPickerName + '"]')}); - await contentPickerLocator.hover(); - await contentPickerLocator.getByLabel('Remove').click(); + const contentPickerLocator = this.entityItem.filter({has: this.page.locator(`[name="${contentPickerName}"]`)}); + await this.hover(contentPickerLocator); + await this.click(contentPickerLocator.getByLabel('Remove')); await this.clickConfirmRemoveButton(); } @@ -667,7 +656,7 @@ export class ContentUiHelper extends UiBaseLocators { // Media Picker async clickChooseMediaPickerButton() { - await this.chooseMediaPickerBtn.click(); + await this.click(this.chooseMediaPickerBtn); } async clickChooseButtonAndSelectMediaWithName(mediaName: string) { @@ -679,9 +668,9 @@ export class ContentUiHelper extends UiBaseLocators { await this.clickChooseMediaPickerButton(); await this.selectMediaWithTestId(mediaKey); } - + async removeMediaPickerByName(mediaPickerName: string) { - await this.page.locator('[name="' + mediaPickerName + '"] [label="Remove"] svg').click(); + await this.click(this.page.locator(`[name="${mediaPickerName}"] [label="Remove"] svg`)); await this.clickConfirmRemoveButton(); } @@ -690,11 +679,11 @@ export class ContentUiHelper extends UiBaseLocators { } async clickResetFocalPointButton() { - await this.resetFocalPointBtn.click(); + await this.click(this.resetFocalPointBtn); } async setFocalPoint(widthPercentage: number = 50, heightPercentage: number = 50) { - await this.page.waitForTimeout(1000); + await this.page.waitForTimeout(ConstantHelper.wait.medium); const element = await this.page.locator('#image').boundingBox(); if (!element) { throw new Error('Element not found'); @@ -706,29 +695,29 @@ export class ContentUiHelper extends UiBaseLocators { const x = element.x + (element.width * widthPercentage) / 100; const y = element.y + (element.height * heightPercentage) / 100; - await this.page.waitForTimeout(200); + await this.page.waitForTimeout(ConstantHelper.wait.minimal); await this.page.mouse.move(centerX, centerY, {steps: 5}); - await this.page.waitForTimeout(200); + await this.page.waitForTimeout(ConstantHelper.wait.minimal); await this.page.mouse.down(); - await this.page.waitForTimeout(200); + await this.page.waitForTimeout(ConstantHelper.wait.minimal); await this.page.mouse.move(x, y); - await this.page.waitForTimeout(200); + await this.page.waitForTimeout(ConstantHelper.wait.minimal); await this.page.mouse.up(); } // Member Picker async clickChooseMemberPickerButton() { - await this.chooseMemberPickerBtn.click(); + await this.click(this.chooseMemberPickerBtn); } async selectMemberByName(memberName: string) { - await this.sidebarModal.getByText(memberName, {exact: true}).click(); + await this.click(this.sidebarModal.getByText(memberName, {exact: true})); } async removeMemberPickerByName(memberName: string) { - const mediaPickerLocator = this.entityItem.filter({has: this.page.locator('[name="' + memberName + '"]')}); - await mediaPickerLocator.hover(); - await mediaPickerLocator.getByLabel('Remove').click(); + const mediaPickerLocator = this.entityItem.filter({has: this.page.locator(`[name="${memberName}"]`)}); + await this.hover(mediaPickerLocator); + await this.click(mediaPickerLocator.getByLabel('Remove')); await this.clickConfirmRemoveButton(); } @@ -740,12 +729,12 @@ export class ContentUiHelper extends UiBaseLocators { // Radiobox async chooseRadioboxOption(optionValue: string) { - await this.page.locator('uui-radio[value="' + optionValue + '"] #button').click(); + await this.click(this.page.locator(`uui-radio[value="${optionValue}"] #button`)); } // Tags async clickPlusIconButton() { - await this.plusIconBtn.click(); + await this.click(this.plusIconBtn); } async enterTag(tagName: string) { @@ -754,18 +743,16 @@ export class ContentUiHelper extends UiBaseLocators { } async removeTagByName(tagName: string) { - await expect(this.tagItems.filter({hasText: tagName}).locator('svg')).toBeVisible(); - await this.tagItems.filter({hasText: tagName}).locator('svg').click(); + await this.click(this.tagItems.filter({hasText: tagName}).locator('svg')); } // Multi URL Picker async clickAddMultiURLPickerButton() { - await this.addMultiURLPickerBtn.click(); + await this.click(this.addMultiURLPickerBtn); } async selectLinkByName(linkName: string) { - await expect(this.sidebarModal.getByText(linkName, {exact: true})).toBeVisible(); - await this.sidebarModal.getByText(linkName, {exact: true}).click(); + await this.click(this.sidebarModal.getByText(linkName, {exact: true})); } async enterLink(value: string, toPress: boolean = false) { @@ -796,24 +783,22 @@ export class ContentUiHelper extends UiBaseLocators { } async removeUrlPickerByName(linkName: string) { - await this.page.locator('[name="' + linkName + '"]').getByLabel('Remove').click(); + await this.click(this.page.locator(`[name="${linkName}"]`).getByLabel('Remove')); await this.clickConfirmRemoveButton(); } async clickEditUrlPickerButtonByName(linkName: string) { - await this.page.locator('[name="' + linkName + '"]').getByLabel('Edit').click(); + await this.click(this.page.locator(`[name="${linkName}"]`).getByLabel('Edit')); } // Upload async clickRemoveFilesButton() { - await expect(this.removeFilesBtn).toBeVisible(); - await this.removeFilesBtn.click(); + await this.click(this.removeFilesBtn); } // True/false async clickToggleButton() { - await expect(this.toggleBtn).toBeVisible(); - await this.toggleBtn.click({force: true}); + await this.click(this.toggleBtn, {force: true}); } async doesToggleHaveLabel(label: string) { @@ -822,7 +807,7 @@ export class ContentUiHelper extends UiBaseLocators { // Multiple Text String async clickAddMultipleTextStringButton() { - await this.addMultipleTextStringBtn.click(); + await this.click(this.addMultipleTextStringBtn); } async enterMultipleTextStringValue(value: string) { @@ -878,28 +863,26 @@ export class ContentUiHelper extends UiBaseLocators { await this.searchTxt.clear(); await this.searchTxt.fill(keyword); await this.searchTxt.press('Enter'); - await this.page.waitForTimeout(500); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickSelectVariantButton() { - await expect(this.selectAVariantBtn).toBeVisible(); - await this.selectAVariantBtn.click(); + await this.click(this.selectAVariantBtn); } async clickVariantAddModeButtonForLanguageName(language: string) { - await this.variantAddModeBtn.getByText(language).click(); - await this.page.waitForTimeout(500); + await this.click(this.variantAddModeBtn.getByText(language)); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickSaveAndCloseButton() { - await this.saveAndCloseBtn.click(); - await this.page.waitForTimeout(500); + await this.click(this.saveAndCloseBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } // List View async clickCreateContentWithName(name: string) { - await expect(this.page.getByLabel('Create ' + name)).toBeVisible(); - await this.page.getByLabel('Create ' + name).click(); + await this.click(this.page.getByLabel(`Create ${name}`)); } async enterNameInContainer(name: string) { @@ -909,7 +892,7 @@ export class ContentUiHelper extends UiBaseLocators { } async goToContentInListViewWithName(contentName: string) { - await this.listView.getByLabel(contentName).click(); + await this.click(this.listView.getByLabel(contentName)); } async doesListViewHaveNoItemsInList() { @@ -921,7 +904,7 @@ export class ContentUiHelper extends UiBaseLocators { } async clickNameButtonInListView() { - await this.nameBtn.click(); + await this.click(this.nameBtn); } async doesFirstItemInListViewHaveName(name: string) { @@ -933,50 +916,43 @@ export class ContentUiHelper extends UiBaseLocators { } async selectContentWithNameInListView(name: string) { - const contentInListViewLocator = this.listViewTableRow.filter({hasText: name}); - await expect(contentInListViewLocator).toBeVisible(); - await contentInListViewLocator.click(); + await this.click(this.listViewTableRow.filter({hasText: name})); } async clickPublishSelectedListItems() { - await this.publishSelectedListItems.click(); + await this.click(this.publishSelectedListItems); } async clickUnpublishSelectedListItems() { - await this.unpublishSelectedListItems.click(); + await this.click(this.unpublishSelectedListItems); } async clickDuplicateToSelectedListItems() { - await expect(this.duplicateToSelectedListItems).toBeVisible(); - // This force click is needed - await this.duplicateToSelectedListItems.click({force: true}); + // Force click is needed + await this.click(this.duplicateToSelectedListItems, {force: true}); } async clickMoveToSelectedListItems() { - await expect(this.moveToSelectedListItems).toBeVisible(); - // This force click is needed - await this.moveToSelectedListItems.click({force: true}); + // Force click is needed + await this.click(this.moveToSelectedListItems, {force: true}); } async clickTrashSelectedListItems() { - await this.trashSelectedListItems.click(); + await this.click(this.trashSelectedListItems); } async selectDocumentWithNameAtRoot(name: string) { await this.openCaretButtonForName('Content'); - const documentWithNameLocator = this.modalContent.getByLabel(name); - await expect(documentWithNameLocator).toBeVisible(); - await documentWithNameLocator.click(); + await this.click(this.modalContent.getByLabel(name)); await this.clickChooseButton(); } async clickTrashButton() { - await expect(this.trashBtn).toBeVisible(); - await this.trashBtn.click(); + await this.click(this.trashBtn); } async clickExactTrashButton() { - await this.exactTrashBtn.click(); + await this.click(this.exactTrashBtn); } async isDocumentListViewVisible(isVisible: boolean = true) { @@ -988,11 +964,9 @@ export class ContentUiHelper extends UiBaseLocators { } async changeDocumentSectionLanguage(newLanguageName: string) { - await this.documentLanguageSelect.click(); - const documentSectionLanguageLocator = this.documentLanguageSelectPopover.getByText(newLanguageName); - await expect(documentSectionLanguageLocator).toBeVisible(); + await this.click(this.documentLanguageSelect); // Force click is needed - await documentSectionLanguageLocator.click({force: true}); + await this.click(this.documentLanguageSelectPopover.getByText(newLanguageName), {force: true}); } async doesDocumentSectionHaveLanguageSelected(languageName: string) { @@ -1017,14 +991,13 @@ export class ContentUiHelper extends UiBaseLocators { } async clickEmptyRecycleBinButton() { - await this.recycleBinMenuItem.hover(); - await expect(this.emptyRecycleBinBtn).toBeVisible(); + await this.hover(this.recycleBinMenuItem); // Force click is needed - await this.emptyRecycleBinBtn.click({force: true}); + await this.click(this.emptyRecycleBinBtn, {force: true}); } async clickConfirmEmptyRecycleBinButton() { - await this.confirmEmptyRecycleBinBtn.click(); + await this.click(this.confirmEmptyRecycleBinBtn); } async isDocumentPropertyEditable(propertyName: string, isEditable: boolean = true) { @@ -1039,7 +1012,7 @@ export class ContentUiHelper extends UiBaseLocators { } async clickContentTab() { - await this.splitView.getByRole('tab', {name: 'Content'}).click(); + await this.click(this.splitView.getByRole('tab', {name: 'Content'})); } async isDocumentTreeEmpty() { @@ -1060,110 +1033,101 @@ export class ContentUiHelper extends UiBaseLocators { } async clickSaveDocumentBlueprintButton() { - await this.documentBlueprintSaveBtn.click(); + await this.click(this.documentBlueprintSaveBtn); } async clickDuplicateToButton() { - await this.duplicateToBtn.click(); + await this.click(this.duplicateToBtn); } async clickDuplicateButton() { - await this.duplicateBtn.click(); + await this.click(this.duplicateBtn); } async clickMoveToButton() { - await this.moveToBtn.click(); + await this.click(this.moveToBtn); } async moveToContentWithName(parentNames: string[], moveTo: string) { for (const contentName of parentNames) { - await this.container.getByLabel('Expand child items for ' + contentName).click(); + await this.click(this.container.getByLabel(`Expand child items for ${contentName}`)); } - await this.container.getByLabel(moveTo).click(); + await this.click(this.container.getByLabel(moveTo)); await this.clickChooseContainerButton(); } async isCaretButtonVisibleForContentName(contentName: string, isVisible: boolean = true) { - await expect(this.page.locator('[label="' + contentName + '"]').getByLabel('Expand child items for ')).toBeVisible({visible: isVisible}); + await expect(this.page.locator(`[label="${contentName}"]`).getByLabel('Expand child items for ')).toBeVisible({visible: isVisible}); } async reloadContentTree() { - await expect(this.contentTreeRefreshBtn).toBeVisible(); // Force click is needed - await this.contentTreeRefreshBtn.click({force: true}); + await this.click(this.contentTreeRefreshBtn, {force: true}); } async clickSortChildrenButton() { - await expect(this.sortChildrenBtn).toBeVisible(); - await this.sortChildrenBtn.click(); + await this.click(this.sortChildrenBtn); } async clickRollbackButton() { - await expect(this.rollbackBtn).toBeVisible(); - await this.rollbackBtn.click(); + await this.click(this.rollbackBtn); } async clickRollbackContainerButton() { - await expect(this.rollbackContainerBtn).toBeVisible(); - await this.rollbackContainerBtn.click(); + await this.click(this.rollbackContainerBtn); } async clickLatestRollBackItem() { - await expect(this.rollbackItem.last()).toBeVisible(); - await this.rollbackItem.last().click(); + await this.click(this.rollbackItem.last()); } async clickPublicAccessButton() { - await expect(this.publicAccessBtn).toBeVisible(); - await this.publicAccessBtn.click(); + await this.click(this.publicAccessBtn); } async addGroupBasedPublicAccess(memberGroupName: string, documentName: string) { - await expect(this.groupBasedProtectionBtn).toBeVisible(); - await this.groupBasedProtectionBtn.click(); + await this.click(this.groupBasedProtectionBtn); await this.clickNextButton(); - await this.chooseMemberGroupBtn.click(); - await this.page.getByLabel(memberGroupName).click(); + await this.click(this.chooseMemberGroupBtn); + await this.click(this.page.getByLabel(memberGroupName)); await this.clickChooseModalButton(); - await this.selectLoginPageDocument.click(); - await this.container.getByLabel(documentName, {exact: true}).click(); + await this.click(this.selectLoginPageDocument); + await this.click(this.container.getByLabel(documentName, {exact: true})); await this.clickChooseModalButton(); - await this.selectErrorPageDocument.click(); - await this.container.getByLabel(documentName, {exact: true}).click(); + await this.click(this.selectErrorPageDocument); + await this.click(this.container.getByLabel(documentName, {exact: true})); await this.clickChooseModalButton(); - await this.containerSaveBtn.click(); + await this.click(this.containerSaveBtn); } async sortChildrenDragAndDrop(dragFromSelector: Locator, dragToSelector: Locator, verticalOffset: number = 0, horizontalOffset: number = 0, steps: number = 5) { - await expect(dragFromSelector).toBeVisible(); - await expect(dragToSelector).toBeVisible(); + await this.waitForVisible(dragFromSelector); + await this.waitForVisible(dragToSelector); const targetLocation = await dragToSelector.boundingBox(); const elementCenterX = targetLocation!.x + targetLocation!.width / 2; const elementCenterY = targetLocation!.y + targetLocation!.height / 2; - await dragFromSelector.hover(); + await this.hover(dragFromSelector); await this.page.mouse.move(10, 10); - await dragFromSelector.hover(); + await this.hover(dragFromSelector); await this.page.mouse.down(); - await this.page.waitForTimeout(400); + await this.page.waitForTimeout(ConstantHelper.wait.debounce); await this.page.mouse.move(elementCenterX + horizontalOffset, elementCenterY + verticalOffset, {steps: steps}); - await this.page.waitForTimeout(400); + await this.page.waitForTimeout(ConstantHelper.wait.debounce); // If we do not have this, the drag and drop will not work - await dragToSelector.hover(); + await this.hover(dragToSelector); await this.page.mouse.up(); } async clickSortButton() { - await expect(this.sortBtn).toBeVisible(); - await this.sortBtn.click(); + await this.click(this.sortBtn); } async doesIndexDocumentInTreeContainName(parentName: string, childName: string, index: number) { - await expect(this.documentTreeItem.locator('[label="' + parentName + '"]').locator('umb-tree-item').nth(index).locator('#label')).toHaveText(childName); + await expect(this.documentTreeItem.locator(`[label="${parentName}"]`).locator('umb-tree-item').nth(index).locator('#label')).toHaveText(childName); } async selectMemberGroup(memberGroupName: string) { - await expect(this.uuiCheckbox.getByLabel(memberGroupName)).toBeVisible(); - await this.uuiCheckbox.getByLabel(memberGroupName).click(); + await this.click(this.uuiCheckbox.getByLabel(memberGroupName)); } async isPermissionInActionsMenuVisible(permissionName: string, isVisible: boolean = true) { @@ -1174,34 +1138,28 @@ export class ContentUiHelper extends UiBaseLocators { } async clickDocumentLinkButton() { - await expect(this.linkToDocumentBtn).toBeVisible(); - await this.linkToDocumentBtn.click(); + await this.click(this.linkToDocumentBtn); } async clickMediaLinkButton() { - await expect(this.linkToMediaBtn).toBeVisible(); - await this.linkToMediaBtn.click(); + await this.click(this.linkToMediaBtn); } async clickManualLinkButton() { - await expect(this.linkToManualBtn).toBeVisible(); - await this.linkToManualBtn.click(); + await this.click(this.linkToManualBtn); } // Block Grid - Block List async clickAddBlockElementButton() { - await expect(this.addBlockElementBtn).toBeVisible(); - await this.addBlockElementBtn.click(); + await this.click(this.addBlockElementBtn); } async clickAddBlockWithNameButton(name: string) { - await expect(this.page.getByLabel('Add '+ name)).toBeVisible(); - await this.page.getByLabel('Add '+ name).click(); + await this.click(this.page.getByLabel(`Add ${name}`)); } - + async clickCreateForModalWithHeadline(headline: string) { - await expect(this.page.locator('[headline="' + headline + '"]').getByLabel('Create')).toBeVisible(); - await this.page.locator('[headline="' + headline + '"]').getByLabel('Create').click(); + await this.click(this.page.locator(`[headline="${headline}"]`).getByLabel('Create')); } async isAddBlockElementButtonVisible(isVisible: boolean = true) { @@ -1221,92 +1179,73 @@ export class ContentUiHelper extends UiBaseLocators { } async clickAddBlockSettingsTabButton() { - await expect(this.addBlockSettingsTabBtn).toBeVisible(); - await this.addBlockSettingsTabBtn.click(); + await this.click(this.addBlockSettingsTabBtn); } async clickEditBlockGridBlockButton() { - await expect(this.blockGridEntry).toBeVisible(); - await this.blockGridEntry.hover(); - await expect(this.editBlockEntryBtn).toBeVisible(); - await this.editBlockEntryBtn.click(); + await this.hover(this.blockGridEntry); + await this.click(this.editBlockEntryBtn); } async clickDeleteBlockGridBlockButton() { - await expect(this.blockGridEntry).toBeVisible(); - await this.blockGridEntry.hover(); - await expect(this.deleteBlockEntryBtn).toBeVisible(); - await this.deleteBlockEntryBtn.click(); + await this.hover(this.blockGridEntry); + await this.click(this.deleteBlockEntryBtn); } async clickEditBlockListBlockButton() { - await expect(this.blockListEntry).toBeVisible(); - await this.blockListEntry.hover(); - await expect(this.editBlockEntryBtn).toBeVisible(); - await this.editBlockEntryBtn.click(); + await this.hover(this.blockListEntry); + await this.click(this.editBlockEntryBtn); } async clickDeleteBlockListBlockButton() { - await expect(this.blockListEntry).toBeVisible(); - await this.blockListEntry.hover(); - await expect(this.deleteBlockEntryBtn).toBeVisible(); - await this.deleteBlockEntryBtn.click(); + await this.hover(this.blockListEntry); + await this.click(this.deleteBlockEntryBtn); } async clickCopyBlockListBlockButton(groupName: string, propertyName: string, blockName: string, index: number = 0) { const blockListBlock = this.workspaceEditTab.filter({hasText: groupName}).locator(this.workspaceEditProperties).filter({hasText: propertyName}).locator(this.blockListEntry).nth(index).filter({hasText: blockName}); - await blockListBlock.hover(); - await expect(blockListBlock.locator(this.copyBlockEntryBtn)).toBeVisible(); - await blockListBlock.locator(this.copyBlockEntryBtn).click({force: true}); - await this.page.waitForTimeout(500); + await this.hover(blockListBlock); + await this.click(blockListBlock.locator(this.copyBlockEntryBtn), {force: true}); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickCopyBlockGridBlockButton(groupName: string, propertyName: string, blockName: string, index: number = 0) { const blockGridBlock = this.workspaceEditTab.filter({hasText: groupName}).locator(this.workspaceEditProperties).filter({hasText: propertyName}).locator(this.blockGridEntry).nth(index).filter({hasText: blockName}); - await blockGridBlock.hover(); - await expect(blockGridBlock.locator(this.copyBlockEntryBtn)).toBeVisible(); - await blockGridBlock.locator(this.copyBlockEntryBtn).click({force: true}); - await this.page.waitForTimeout(500); + await this.hover(blockGridBlock); + await this.click(blockGridBlock.locator(this.copyBlockEntryBtn), {force: true}); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickPasteFromClipboardButtonForProperty(groupName: string, propertyName: string) { - await this.page.waitForTimeout(500); + await this.page.waitForTimeout(ConstantHelper.wait.short); const property = this.workspaceEditTab.filter({hasText: groupName}).locator(this.property).filter({hasText: propertyName}); - await expect(property).toBeVisible(); - await expect(property.locator(this.pasteFromClipboardBtn)).toBeVisible(); - await property.locator(this.pasteFromClipboardBtn).click({force: true}); + await this.click(property.locator(this.pasteFromClipboardBtn), {force: true}); } async clickActionsMenuForProperty(groupName: string, propertyName: string) { const property = this.workspaceEditTab.filter({hasText: groupName}).locator(this.workspaceEditProperties).filter({hasText: propertyName}); - await property.hover(); - await expect(property.locator(this.openActionsMenu)).toBeVisible(); - await property.locator(this.openActionsMenu).click({force: true}); + await this.hover(property); + await this.click(property.locator(this.openActionsMenu), {force: true}); } async clickAddBlockGridElementWithName(elementTypeName: string) { - await expect(this.page.getByRole('link', {name: 'Add ' + elementTypeName, exact: true})).toBeVisible(); - await this.page.getByRole('link', {name: 'Add ' + elementTypeName, exact: true}).click(); + await this.click(this.page.getByRole('link', {name: `Add ${elementTypeName}`, exact: true})); } async clickEditBlockListEntryWithName(blockListElementName: string) { - await expect(this.blockListEntry.filter({hasText: blockListElementName}).getByLabel('edit')).toBeVisible(); - await this.blockListEntry.filter({hasText: blockListElementName}).getByLabel('edit').click({force: true}); + await this.click(this.blockListEntry.filter({hasText: blockListElementName}).getByLabel('edit'), {force: true}); } async clickSelectBlockElementWithName(elementTypeName: string) { - await expect(this.page.getByRole('button', {name: elementTypeName, exact: true})).toBeVisible(); - await this.page.getByRole('button', {name: elementTypeName, exact: true}).click(); + await this.click(this.page.getByRole('button', {name: elementTypeName, exact: true})); } async clickSelectBlockElementInAreaWithName(elementTypeName: string) { - await expect(this.container.getByRole('button', {name: elementTypeName, exact: true})).toBeVisible(); - await this.container.getByRole('button', {name: elementTypeName, exact: true}).click(); + await this.click(this.container.getByRole('button', {name: elementTypeName, exact: true})); } async clickBlockElementWithName(elementTypeName: string) { - await expect(this.page.getByRole('link', {name: elementTypeName, exact: true})).toBeVisible(); - await this.page.getByRole('link', {name: elementTypeName, exact: true}).click({force: true}); + await this.click(this.page.getByRole('link', {name: elementTypeName, exact: true}), {force: true}); } async enterPropertyValue(propertyName: string, value: string) { @@ -1318,21 +1257,21 @@ export class ContentUiHelper extends UiBaseLocators { async doesBlockContainBlockInAreaWithName(blockWithAreaName: string, areaName: string, blockInAreaName: string, index: number = 0) { const blockWithArea = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: blockWithAreaName})).nth(index); - const area = blockWithArea.locator(this.blockGridAreasContainer).locator('[data-area-alias="' + areaName + '"]'); + const area = blockWithArea.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); const blockInArea = area.locator(this.blockGridEntry.filter({hasText: blockInAreaName})); await expect(blockInArea).toBeVisible(); } async doesBlockContainBlockCountInArea(blockWithAreaName: string, areaName: string, blocksInAreaCount: number, index: number = 0) { const blockWithArea = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: blockWithAreaName})).nth(index); - const area = blockWithArea.locator(this.blockGridAreasContainer).locator('[data-area-alias="' + areaName + '"]'); + const area = blockWithArea.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); const blocks = area.locator(this.blockGridEntry); await expect(blocks).toHaveCount(blocksInAreaCount); } async doesBlockContainCountOfBlockInArea(blockWithAreaName: string, areaName: string, blockInAreaName: string, count: number, index: number = 0) { const blockWithArea = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: blockWithAreaName})).nth(index); - const area = blockWithArea.locator(this.blockGridAreasContainer).locator('[data-area-alias="' + areaName + '"]'); + const area = blockWithArea.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); const blockInArea = area.locator(this.blockGridEntry.filter({hasText: blockInAreaName})); await expect(blockInArea).toHaveCount(count); } @@ -1349,45 +1288,44 @@ export class ContentUiHelper extends UiBaseLocators { async getBlockDataElementKeyInArea(parentBlockName: string, areaName: string, blockName: string, parentIndex: number = 0, childIndex: number = 0) { const parentBlock = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: parentBlockName})).nth(parentIndex); - const area = parentBlock.locator(this.blockGridAreasContainer).locator('[data-area-alias="' + areaName + '"]'); + const area = parentBlock.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); const block = area.locator(this.blockGridEntry.filter({hasText: blockName})).nth(childIndex); return block.getAttribute('data-element-key'); } async removeBlockFromArea(parentBlockName: string, areaName: string, blockName: string, parentIndex: number = 0, childIndex: number = 0) { const parentBlock = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: parentBlockName})).nth(parentIndex); - const area = parentBlock.locator(this.blockGridAreasContainer).locator('[data-area-alias="' + areaName + '"]'); + const area = parentBlock.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); const block = area.locator(this.blockGridEntry.filter({hasText: blockName})).nth(childIndex); - await block.hover(); - await block.getByLabel('delete').click({force: true}); + await this.hover(block); + await this.click(block.getByLabel('delete'), {force: true}); } async doesBlockAreaContainColumnSpan(blockWithAreaName: string, areaName: string, columnSpan: number, index: number = 0) { const blockWithArea = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: blockWithAreaName})).nth(index); - const area = blockWithArea.locator(this.blockGridAreasContainer).locator('[data-area-alias="' + areaName + '"]'); + const area = blockWithArea.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); await expect(area).toHaveAttribute('data-area-col-span', columnSpan.toString()); } async doesBlockAreaContainRowSpan(blockWithAreaName: string, areaName: string, rowSpan: number, index: number = 0) { const blockWithArea = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: blockWithAreaName})).nth(index); - const area = blockWithArea.locator(this.blockGridAreasContainer).locator('[data-area-alias="' + areaName + '"]'); + const area = blockWithArea.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); await expect(area).toHaveAttribute('data-area-row-span', rowSpan.toString()); } async clickInlineAddToAreaButton(parentBlockName: string, areaName: string, parentIndex: number = 0, buttonIndex: number = 1) { const parentBlock = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: parentBlockName})).nth(parentIndex); - const area = parentBlock.locator(this.blockGridAreasContainer).locator('[data-area-alias="' + areaName + '"]'); - await area.locator(this.inlineCreateBtn).nth(buttonIndex).click(); + const area = parentBlock.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); + await this.click(area.locator(this.inlineCreateBtn).nth(buttonIndex)); } async addBlockToAreasWithExistingBlock(blockWithAreaName: string, areaName: string, parentIndex: number = 0, addToIndex: number = 0) { const blockWithArea = this.blockGridEntry.locator(this.blockGridBlock).filter({hasText: blockWithAreaName}).nth(parentIndex); - await expect(blockWithArea).toBeVisible(); - await blockWithArea.hover(); - const area = blockWithArea.locator(this.blockGridAreasContainer).locator('[data-area-alias="' + areaName + '"]'); + await this.hover(blockWithArea); + const area = blockWithArea.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); const addBlockBtn = area.locator(this.inlineCreateBtn).nth(addToIndex); - await addBlockBtn.hover({force: true}); - await addBlockBtn.click({force: true}); + await this.hover(addBlockBtn, {force: true}); + await this.click(addBlockBtn, {force: true}); } async doesBlockGridBlockWithAreaContainCreateLabel(blockWithAreaName: string, createLabel: string, index: number = 0) { @@ -1400,23 +1338,19 @@ export class ContentUiHelper extends UiBaseLocators { } async clickCreateButtonForModalWithElementTypeNameAndGroupName(headlineName: string, groupName: string) { - await expect(this.blockWorkspace.filter({hasText: 'Add ' + headlineName}).filter({hasText: groupName}).getByLabel('Create')).toBeVisible(); - await this.blockWorkspace.filter({hasText: 'Add ' + headlineName}).filter({hasText: groupName}).getByLabel('Create').click(); + await this.click(this.blockWorkspace.filter({hasText: `Add ${headlineName}`}).filter({hasText: groupName}).getByLabel('Create')); } async clickUpdateButtonForModalWithElementTypeNameAndGroupName(headlineName: string, groupName: string) { - await expect(this.blockWorkspace.filter({hasText: 'Edit ' + headlineName}).filter({hasText: groupName}).locator(this.updateBtn)).toBeVisible(); - await this.blockWorkspace.filter({hasText: 'Edit ' + headlineName}).filter({hasText: groupName}).locator(this.updateBtn).click(); + await this.click(this.blockWorkspace.filter({hasText: `Edit ${headlineName}`}).filter({hasText: groupName}).locator(this.updateBtn)); } async clickExactCopyButton() { - await expect(this.exactCopyBtn).toBeVisible(); - await this.exactCopyBtn.click(); + await this.click(this.exactCopyBtn); } async clickExactReplaceButton() { - await expect(this.replaceExactBtn).toBeVisible(); - await this.replaceExactBtn.click(); + await this.click(this.replaceExactBtn); } async doesClipboardHaveCopiedBlockWithName(contentName: string, propertyName: string, blockName: string, index: number = 0) { @@ -1433,24 +1367,22 @@ export class ContentUiHelper extends UiBaseLocators { async selectClipboardEntryWithName(contentName: string, propertyName: string, blockName: string, index: number = 0) { await this.doesClipboardHaveCopiedBlockWithName(contentName, propertyName, blockName, index); - await this.clipboardEntryPicker.getByLabel(`${contentName} - ${propertyName} - ${blockName}`).nth(index).click(); + await this.click(this.clipboardEntryPicker.getByLabel(`${contentName} - ${propertyName} - ${blockName}`).nth(index)); } async selectClipboardEntriesWithName(contentName: string, propertyName: string, index: number = 0) { await this.doesClipboardHaveCopiedBlocks(contentName, propertyName, index); - await this.clipboardEntryPicker.getByLabel(`${contentName} - ${propertyName}`).nth(index).click(); + await this.click(this.clipboardEntryPicker.getByLabel(`${contentName} - ${propertyName}`).nth(index)); } async goToBlockGridBlockWithName(groupName: string, propertyName: string, blockName: string, index: number = 0) { const blockGridBlock = this.workspaceEditTab.filter({hasText: groupName}).locator(this.workspaceEditProperties).filter({hasText: propertyName}).locator(this.blockGridEntry).nth(index).filter({hasText: blockName}); - await expect(blockGridBlock).toBeVisible(); - await blockGridBlock.click(); + await this.click(blockGridBlock); } async goToBlockListBlockWithName(groupName: string, propertyName: string, blockName: string, index: number = 0) { const blocklistBlock = this.workspaceEditTab.filter({hasText: groupName}).locator(this.workspaceEditProperties).filter({hasText: propertyName}).locator(this.blockListEntry).nth(index).filter({hasText: blockName}); - await expect(blocklistBlock).toBeVisible(); - await blocklistBlock.click(); + await this.click(blocklistBlock); } async doesBlockEditorBlockWithNameContainValue(groupName: string, propertyName: string, inputType: string = ConstantHelper.inputTypes.general, value) { @@ -1458,13 +1390,11 @@ export class ContentUiHelper extends UiBaseLocators { } async clickCloseButton() { - await expect(this.closeBtn).toBeVisible(); - await this.closeBtn.click(); + await this.click(this.closeBtn); } async clickPasteButton() { - await expect(this.pasteBtn).toBeVisible(); - await this.pasteBtn.click({force: true}); + await this.click(this.pasteBtn, {force: true}); } async doesBlockListPropertyHaveBlockAmount(groupName: string, propertyName: string, amount: number) { @@ -1480,27 +1410,21 @@ export class ContentUiHelper extends UiBaseLocators { } async clickInsertBlockButton() { - await expect(this.insertBlockBtn).toBeVisible(); - await this.insertBlockBtn.click(); + await this.click(this.insertBlockBtn); } // TipTap async enterRTETipTapEditor(value: string) { - await expect(this.tipTapEditor).toBeVisible(); - await this.tipTapEditor.clear(); - await this.tipTapEditor.fill(value); + await this.enterText(this.tipTapEditor, value); } - + async enterRTETipTapEditorWithName(name: string , value: string){ - const tipTapEditorLocator = this.page.locator('[data-mark="property:' + name + '"]').locator(this.tipTapEditor); - await expect(tipTapEditorLocator).toBeVisible(); - await tipTapEditorLocator.clear(); - await tipTapEditorLocator.fill(value); + const tipTapEditorLocator = this.page.locator(`[data-mark="property:${name}"]`).locator(this.tipTapEditor); + await this.enterText(tipTapEditorLocator, value); } async clickTipTapToolbarIconWithTitle(iconTitle: string) { - await expect(this.tipTapPropertyEditor.getByTitle(iconTitle, {exact: true}).locator('svg')).toBeVisible(); - await this.tipTapPropertyEditor.getByTitle(iconTitle, {exact: true}).locator('svg').click(); + await this.click(this.tipTapPropertyEditor.getByTitle(iconTitle, {exact: true}).locator('svg')); } async doesUploadedSvgThumbnailHaveSrc(imageSrc: string) { @@ -1509,23 +1433,23 @@ export class ContentUiHelper extends UiBaseLocators { } async doesRichTextEditorBlockContainLabel(richTextEditorAlias: string, label: string) { - await expect(this.page.getByTestId('property:' + richTextEditorAlias).locator(this.rteBlock)).toContainText(label); + await expect(this.page.getByTestId(`property:${richTextEditorAlias}`).locator(this.rteBlock)).toContainText(label); } async doesBlockEditorModalContainEditorSize(editorSize: string, elementName: string) { - await expect(this.backofficeModalContainer.locator('[size="' + editorSize + '"]').locator('[headline="Add ' + elementName + '"]')).toBeVisible(); + await expect(this.backofficeModalContainer.locator(`[size="${editorSize}"]`).locator(`[headline="Add ${elementName}"]`)).toBeVisible(); } async doesBlockEditorModalContainInline(richTextEditorAlias: string, elementName: string) { - await expect(this.page.getByTestId('property:' + richTextEditorAlias).locator(this.tiptapInput).locator(this.rteBlockInline)).toContainText(elementName); + await expect(this.page.getByTestId(`property:${richTextEditorAlias}`).locator(this.tiptapInput).locator(this.rteBlockInline)).toContainText(elementName); } async doesBlockHaveBackgroundColor(elementName: string, backgroundColor: string) { - await expect(this.page.locator('umb-block-type-card', {hasText: elementName}).locator('[style="background-color:' + backgroundColor + ';"]')).toBeVisible(); + await expect(this.page.locator('umb-block-type-card', {hasText: elementName}).locator(`[style="background-color:${backgroundColor};"]`)).toBeVisible(); } async doesBlockHaveIconColor(elementName: string, backgroundColor: string) { - await expect(this.page.locator('umb-block-type-card', {hasText: elementName}).locator('[color="' + backgroundColor + '"]')).toBeVisible(); + await expect(this.page.locator('umb-block-type-card', {hasText: elementName}).locator(`[color="${backgroundColor}"]`)).toBeVisible(); } async addDocumentDomain(domainName: string, languageName: string) { @@ -1538,18 +1462,15 @@ export class ContentUiHelper extends UiBaseLocators { // Scheduled Publishing async clickViewMoreOptionsButton() { - await expect(this.viewMoreOptionsBtn).toBeVisible(); - await this.viewMoreOptionsBtn.click(); + await this.click(this.viewMoreOptionsBtn); } async clickSchedulePublishButton() { - await expect(this.schedulePublishBtn).toBeVisible(); - await this.schedulePublishBtn.click(); + await this.click(this.schedulePublishBtn); } async clickSchedulePublishModalButton() { - await expect(this.schedulePublishModalBtn).toBeVisible(); - await this.schedulePublishModalBtn.click(); + await this.click(this.schedulePublishModalBtn); } async enterPublishTime(time: string, index: number = 0) { @@ -1585,8 +1506,7 @@ export class ContentUiHelper extends UiBaseLocators { } async clickSelectAllCheckbox() { - await expect(this.selectAllCheckbox).toBeVisible(); - await this.selectAllCheckbox.click(); + await this.click(this.selectAllCheckbox); } async doesSchedulePublishModalButtonContainDisabledTag(hasDisabledTag: boolean = false) { @@ -1598,21 +1518,19 @@ export class ContentUiHelper extends UiBaseLocators { async clickInlineBlockCaretButtonForName(blockEditorName: string, index: number = 0) { const caretButtonLocator = this.blockListEntry.filter({hasText: blockEditorName}).nth(index).locator('uui-symbol-expand svg'); - await expect(caretButtonLocator).toBeVisible(); - await caretButtonLocator.click(); + await this.click(caretButtonLocator); } - + async doesTiptapHaveWordCount(count: number) { - await expect(this.tiptapStatusbarWordCount).toHaveText(count.toString() + ' words'); + await expect(this.tiptapStatusbarWordCount).toHaveText(`${count} words`); } - - async doesTiptapHaveCharacterCount(count: number) { - await expect(this.tiptapStatusbarWordCount).toHaveText(count.toString() + ' characters'); + + async doesTiptapHaveCharacterCount(count: number) { + await expect(this.tiptapStatusbarWordCount).toHaveText(`${count} characters`); } async clickTiptapWordCountButton() { - await expect(this.tiptapStatusbarWordCount).toBeVisible(); - await this.tiptapStatusbarWordCount.click(); + await this.click(this.tiptapStatusbarWordCount); } async doesElementPathHaveText(text: string) { @@ -1620,22 +1538,19 @@ export class ContentUiHelper extends UiBaseLocators { } async clickConfirmToPublishButton() { - await this.confirmToPublishBtn.click(); + await this.click(this.confirmToPublishBtn); } async clickPublishWithDescendantsButton() { - await expect(this.publishWithDescendantsBtn).toBeVisible(); - await this.publishWithDescendantsBtn.click(); + await this.click(this.publishWithDescendantsBtn); } async clickIncludeUnpublishedDescendantsToggle() { - await expect(this.includeUnpublishedDescendantsToggle).toBeVisible(); - await this.includeUnpublishedDescendantsToggle.click(); + await this.click(this.includeUnpublishedDescendantsToggle); } async clickPublishWithDescendantsModalButton() { - await expect(this.publishWithDescendantsModalBtn).toBeVisible(); - await this.publishWithDescendantsModalBtn.click(); + await this.click(this.publishWithDescendantsModalBtn); } async doesDocumentVariantLanguageItemHaveCount(count: number) { @@ -1647,36 +1562,30 @@ export class ContentUiHelper extends UiBaseLocators { } async clickSchedulePublishLanguageButton(languageName: string) { - await expect(this.page.getByRole('menu').filter({hasText: languageName})).toBeVisible(); - await this.page.getByRole('menu').filter({hasText: languageName}).click(); + await this.click(this.page.getByRole('menu').filter({hasText: languageName})); } async clickBlockCardWithName(name: string, toForce: boolean = false) { const blockWithNameLocator = this.page.locator('uui-card-block-type', {hasText: name}); - await expect(blockWithNameLocator).toBeVisible(); - await blockWithNameLocator.click({force: toForce}); + await this.click(blockWithNameLocator, {force: toForce}); } async clickStyleSelectButton() { - await expect(this.styleSelectBtn).toBeVisible(); - await this.styleSelectBtn.click(); + await this.click(this.styleSelectBtn); } async clickCascadingMenuItemWithName(name: string) { - const menuItemLocator = this.cascadingMenuContainer.locator('uui-menu-item[label="' + name + '"]'); - await expect(menuItemLocator).toBeVisible(); - await menuItemLocator.click(); + const menuItemLocator = this.cascadingMenuContainer.locator(`uui-menu-item[label="${name}"]`); + await this.click(menuItemLocator); } async hoverCascadingMenuItemWithName(name: string) { - const menuItemLocator = this.cascadingMenuContainer.locator('uui-menu-item[label="' + name + '"]'); - await expect(menuItemLocator).toBeVisible(); - await menuItemLocator.hover(); + const menuItemLocator = this.cascadingMenuContainer.locator(`uui-menu-item[label="${name}"]`); + await this.hover(menuItemLocator); } async selectAllRTETipTapEditorText() { - await expect(this.tipTapEditor).toBeVisible(); - await this.tipTapEditor.click(); + await this.click(this.tipTapEditor); await this.page.keyboard.press('Control+A'); } @@ -1689,7 +1598,7 @@ export class ContentUiHelper extends UiBaseLocators { } async clearTipTapEditor() { - await expect(this.tipTapEditor).toBeVisible(); + await this.waitForVisible(this.tipTapEditor); // We use the middle mouse button click so we don't accidentally open a block in the RTE. This solution avoids that. await this.tipTapEditor.click({button: "middle"}); await this.page.keyboard.press('Control+A'); @@ -1698,8 +1607,7 @@ export class ContentUiHelper extends UiBaseLocators { async clickBlockElementInRTEWithName(elementTypeName: string) { const blockElementLocator = this.page.locator('uui-ref-node umb-ufm-render').filter({hasText: elementTypeName}); - await expect(blockElementLocator).toBeVisible(); - await blockElementLocator.click({force: true}); + await this.click(blockElementLocator, {force: true}); } async doesModalFormValidationMessageContainText(text: string) { @@ -1748,11 +1656,10 @@ export class ContentUiHelper extends UiBaseLocators { await expect(locator).toBeVisible(); await expect(locator).toHaveText(valueText); } - + async clickPropertyActionWithName(name: string) { - const actionLocator = this.propertyActionMenu.locator('umb-property-action uui-menu-item[label="' + name + '"]'); - await expect(actionLocator).toBeVisible(); - await actionLocator.click(); + const actionLocator = this.propertyActionMenu.locator(`umb-property-action uui-menu-item[label="${name}"]`); + await this.click(actionLocator); } async isContentWithNameVisibleInList(contentName: string, isVisible: boolean = true) { @@ -1760,8 +1667,7 @@ export class ContentUiHelper extends UiBaseLocators { } async selectDocumentBlueprintWithName(blueprintName: string) { - await expect(this.documentCreateOptionsModal.locator('uui-menu-item', {hasText: blueprintName})).toBeVisible(); - await this.documentCreateOptionsModal.locator('uui-menu-item', {hasText: blueprintName}).click(); + await this.click(this.documentCreateOptionsModal.locator('uui-menu-item', {hasText: blueprintName})); } async doesDocumentModalHaveText(text: string) { @@ -1777,23 +1683,22 @@ export class ContentUiHelper extends UiBaseLocators { } async clickPaginationNextButton(){ - await expect(this.nextPaginationBtn).toBeVisible(); - await this.nextPaginationBtn.click(); + await this.click(this.nextPaginationBtn); } - + // Entity Data Picker async chooseCollectionMenuItemWithName(name: string) { await this.clickChooseButton(); - await this.collectionMenu.locator('umb-collection-menu-item', {hasText: name}).click(); + await this.click(this.collectionMenu.locator('umb-collection-menu-item', {hasText: name})); await this.clickChooseContainerButton(); } - + async chooseTreeMenuItemWithName(name: string, parentNames: string[] = []) { await this.clickChooseButton(); for (const itemName of parentNames) { - await this.entityPickerTree.locator('umb-tree-item').getByLabel('Expand child items for ' + itemName).click(); + await this.click(this.entityPickerTree.locator('umb-tree-item').getByLabel(`Expand child items for ${itemName}`)); } - await this.container.getByLabel(name).click(); + await this.click(this.container.getByLabel(name)); await this.clickChooseContainerButton(); } @@ -1802,33 +1707,29 @@ export class ContentUiHelper extends UiBaseLocators { } async clickDocumentNotificationOptionWithName(name: string) { - const notificationOptionLocator = this.page.locator('umb-document-notifications-modal [id$="' + name + '"]').locator('#toggle'); - await expect(notificationOptionLocator).toBeVisible(); - await notificationOptionLocator.click(); + const notificationOptionLocator = this.page.locator(`umb-document-notifications-modal [id$="${name}"]`).locator('#toggle'); + await this.click(notificationOptionLocator); } async switchLanguage(languageName: string) { - await expect(this.languageToggle).toBeVisible(); - await this.languageToggle.click(); + await this.click(this.languageToggle); const languageOptionLocator = this.contentVariantDropdown.locator('.culture-variant').filter({hasText: languageName}); - await expect(languageOptionLocator).toBeVisible(); - await languageOptionLocator.click(); + await this.click(languageOptionLocator); await expect(languageOptionLocator).toContainClass('selected'); } async clickAddBlockListElementWithName(blockName: string) { - const createNewButtonLocator = this.page.getByTestId('property:' + blockName.toLowerCase()).getByLabel('Create new'); - await expect(createNewButtonLocator).toBeVisible(); - await createNewButtonLocator.click(); + const createNewButtonLocator = this.page.getByTestId(`property:${blockName.toLowerCase()}`).getByLabel('Create new'); + await this.click(createNewButtonLocator); } async isAddBlockListElementWithNameDisabled(blockName: string) { - const createNewButtonLocator = this.page.getByTestId('property:' + blockName.toLowerCase()).locator('uui-button[label="Create new"]'); + const createNewButtonLocator = this.page.getByTestId(`property:${blockName.toLowerCase()}`).locator('uui-button[label="Create new"]'); await expect(createNewButtonLocator).toHaveAttribute('disabled'); } async isAddBlockListElementWithNameVisible(blockName: string) { - const createNewButtonLocator = this.page.getByTestId('property:' + blockName.toLowerCase()).locator('uui-button[label="Create new"]'); + const createNewButtonLocator = this.page.getByTestId(`property:${blockName.toLowerCase()}`).locator('uui-button[label="Create new"]'); await expect(createNewButtonLocator).toBeVisible(); await expect(createNewButtonLocator).not.toHaveAttribute('disabled'); } diff --git a/lib/helpers/CurrentUserProfileUiHelper.ts b/lib/helpers/CurrentUserProfileUiHelper.ts index 2b16ac19..1139306c 100644 --- a/lib/helpers/CurrentUserProfileUiHelper.ts +++ b/lib/helpers/CurrentUserProfileUiHelper.ts @@ -10,18 +10,13 @@ export class CurrentUserProfileUiHelper extends UiBaseLocators { } async clickChangePasswordButton() { - await expect(this.changePasswordBtn).toBeVisible(); - await this.changePasswordBtn.click(); + await this.click(this.changePasswordBtn); } async changePassword(currentPassword: string, newPassword: string) { - await expect(this.currentPasswordTxt).toBeVisible(); - await this.currentPasswordTxt.clear(); - await this.currentPasswordTxt.fill(currentPassword); - await this.newPasswordTxt.clear(); - await this.newPasswordTxt.fill(newPassword); - await this.confirmPasswordTxt.clear(); - await this.confirmPasswordTxt.fill(newPassword); + await this.enterText(this.currentPasswordTxt, currentPassword); + await this.enterText(this.newPasswordTxt, newPassword); + await this.enterText(this.confirmPasswordTxt, newPassword); await this.clickConfirmButton(); } } \ No newline at end of file diff --git a/lib/helpers/DataTypeUiHelper.ts b/lib/helpers/DataTypeUiHelper.ts index 39659805..aa7fdd67 100644 --- a/lib/helpers/DataTypeUiHelper.ts +++ b/lib/helpers/DataTypeUiHelper.ts @@ -1,5 +1,6 @@ import {Page, Locator, expect} from "@playwright/test"; import {UiBaseLocators} from "./UiBaseLocators"; +import {ConstantHelper} from "./ConstantHelper"; export class DataTypeUiHelper extends UiBaseLocators { private readonly moveToBtn: Locator; @@ -344,18 +345,15 @@ export class DataTypeUiHelper extends UiBaseLocators { async goToDataType(dataTypeName: string) { await this.clickRootFolderCaretButton(); - await expect(this.sectionSidebar.getByLabel(dataTypeName, {exact: true})).toBeVisible(); - await this.sectionSidebar.getByLabel(dataTypeName, {exact: true}).click(); + await this.click(this.sectionSidebar.getByLabel(dataTypeName, {exact: true})); } async clickMoveToButton() { - await expect(this.moveToBtn).toBeVisible(); - await this.moveToBtn.click(); + await this.click(this.moveToBtn); } async clickDuplicateToButton() { - await expect(this.duplicateToBtn).toBeVisible(); - await this.duplicateToBtn.click(); + await this.click(this.duplicateToBtn); } async waitForDataTypeToBeCreated() { @@ -363,15 +361,13 @@ export class DataTypeUiHelper extends UiBaseLocators { } async isDataTypeTreeItemVisible(name: string, isVisible: boolean = true) { - { - const hasShowChildren = await this.dataTypeTreeRoot.getAttribute('show-children') !== null; + const hasShowChildren = await this.dataTypeTreeRoot.getAttribute('show-children') !== null; - if (!hasShowChildren) { - await this.dataTypeTreeRoot.locator(this.caretBtn).first().click(); - } - - await this.isTreeItemVisible(name, isVisible); + if (!hasShowChildren) { + await this.click(this.dataTypeTreeRoot.locator(this.caretBtn).first()); } + + await this.isTreeItemVisible(name, isVisible); } async waitForDataTypeToBeDeleted() { @@ -383,31 +379,25 @@ export class DataTypeUiHelper extends UiBaseLocators { } async clickNewDataTypeButton() { - await expect(this.newDataTypeBtn).toBeVisible(); - await this.newDataTypeBtn.click(); + await this.click(this.newDataTypeBtn); } async clickNewDataTypeFolderButton() { - await expect(this.newFolderBtn).toBeVisible(); - await this.newFolderBtn.click(); + await this.click(this.newFolderBtn); } async enterDataTypeName(name: string) { - await expect(this.dataTypeNameTxt).toBeVisible(); - await this.dataTypeNameTxt.click(); - await this.dataTypeNameTxt.clear(); - await this.dataTypeNameTxt.fill(name); + await this.click(this.dataTypeNameTxt); + await this.enterText(this.dataTypeNameTxt, name); } async clickCreateFolderButton() { - await expect(this.createFolderBtn).toBeVisible(); - await this.createDataTypeFolderBtn.click(); - await this.page.waitForTimeout(500); // Wait for the action to complete + await this.click(this.createDataTypeFolderBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickUpdateFolderButton() { - await expect(this.updateDataTypeFolderBtn).toBeVisible(); - await this.updateDataTypeFolderBtn.click(); + await this.click(this.updateDataTypeFolderBtn); } async deleteDataType(name: string) { @@ -422,31 +412,28 @@ export class DataTypeUiHelper extends UiBaseLocators { async moveDataTypeToFolder(folderName: string) { await this.clickMoveToActionMenuOption(); - await expect(this.sidebarModal.getByText(folderName, {exact: true})).toBeVisible(); - await this.sidebarModal.getByText(folderName, {exact: true}).click(); - await this.chooseModalBtn.click(); + await this.click(this.sidebarModal.getByText(folderName, {exact: true})); + await this.click(this.chooseModalBtn); } async duplicateDataTypeToFolder(folderName: string) { await this.clickDuplicateToActionMenuOption(); - await expect(this.sidebarModal.getByText(folderName, {exact: true})).toBeVisible(); - await this.sidebarModal.getByText(folderName, {exact: true}).click(); - await this.duplicateBtn.click(); + await this.click(this.sidebarModal.getByText(folderName, {exact: true})); + await this.click(this.duplicateBtn); } async addMediaStartNode(mediaName: string) { - await expect(this.mediaCardItems.filter({hasText: mediaName})).toBeVisible(); - await this.mediaCardItems.filter({hasText: mediaName}).click(); + await this.click(this.mediaCardItems.filter({hasText: mediaName})); await this.clickChooseModalButton(); } async addContentStartNode(contentName: string) { await this.clickTextButtonWithName(contentName); - await this.chooseModalBtn.click(); + await this.click(this.chooseModalBtn); } async clickSelectAPropertyEditorButton() { - await this.selectAPropertyEditorBtn.click(); + await this.click(this.selectAPropertyEditorBtn); } async selectAPropertyEditor(propertyName: string, filterKeyword?: string) { @@ -456,16 +443,16 @@ export class DataTypeUiHelper extends UiBaseLocators { // Approved Color async clickIncludeLabelsToggle() { - await this.includeLabelsToggle.click(); + await this.click(this.includeLabelsToggle); } async removeColorByValue(value: string) { - await this.page.locator('[value="' + value + '"] uui-button svg').click(); - await this.confirmToDeleteBtn.click(); + await this.click(this.page.locator(`[value="${value}"] uui-button svg`)); + await this.click(this.confirmToDeleteBtn); } async addColor(value: string) { - await this.addColorBtn.click(); + await this.click(this.addColorBtn); await this.colorValueTxt.clear(); await this.colorValueTxt.fill(value); } @@ -477,7 +464,7 @@ export class DataTypeUiHelper extends UiBaseLocators { // Date Picker async clickOffsetTimeToggle() { - await this.offsetTimeToggle.click(); + await this.click(this.offsetTimeToggle); } async enterDateFormatValue(value: string) { @@ -493,14 +480,14 @@ export class DataTypeUiHelper extends UiBaseLocators { async chooseOrderDirection(isAscending: boolean) { if (isAscending) { - await this.ascendingRadioBtn.click(); + await this.click(this.ascendingRadioBtn); } else { - await this.descendingRadioBtn.click(); + await this.click(this.descendingRadioBtn); } } async addColumnDisplayed(contentType: string, contentName: string, propertyAlias: string) { - await this.chooseColumnsDisplayedBtn.click(); + await this.click(this.chooseColumnsDisplayedBtn); await this.clickTextButtonWithName(contentType); await this.clickTextButtonWithName(contentName); await this.clickChooseContainerButton(); @@ -508,18 +495,16 @@ export class DataTypeUiHelper extends UiBaseLocators { } async removeColumnDisplayed(propertyAlias: string) { - await this.columnsDisplayedItems.filter({has: this.page.getByText(propertyAlias, {exact: true})}).getByText('Remove').click(); + await this.click(this.columnsDisplayedItems.filter({has: this.page.getByText(propertyAlias, {exact: true})}).getByText('Remove')); } async addLayouts(layoutName: string) { - await expect(this.chooseLayoutsBtn).toBeVisible(); - await this.chooseLayoutsBtn.click(); - await expect(this.page.locator('[name="' + layoutName + '"]')).toBeVisible(); - await this.page.locator('[name="' + layoutName + '"]').click(); + await this.click(this.chooseLayoutsBtn); + await this.click(this.page.locator(`[name="${layoutName}"]`)); } async removeLayouts(layoutAlias: string) { - await this.layoutsItems.filter({has: this.page.getByText(layoutAlias, {exact: true})}).getByText('Remove').click(); + await this.click(this.layoutsItems.filter({has: this.page.getByText(layoutAlias, {exact: true})}).getByText('Remove')); } async chooseOrderByValue(value: string) { @@ -532,26 +517,25 @@ export class DataTypeUiHelper extends UiBaseLocators { } async clickShowContentWorkspaceViewFirstToggle() { - await this.showWorkspaceViewFirstToggle.click(); + await this.click(this.showWorkspaceViewFirstToggle); } async clickEditInInfiniteEditorToggle() { - await this.editInInfiniteEditorToggle.click(); + await this.click(this.editInInfiniteEditorToggle); } async clickBulkActionPermissionsToggleByValue(value: string) { - await this.page.locator("uui-toggle[label='" + value + "'] #toggle").click(); + await this.click(this.page.locator(`uui-toggle[label='${value}'] #toggle`)); } async clickSelectIconButton() { - await expect(this.selectIconBtn).toBeVisible(); // Force click is needed - await this.selectIconBtn.click({force: true}); + await this.click(this.selectIconBtn, {force: true}); } async chooseWorkspaceViewIconByValue(value: string) { - await this.page.locator('[label="' + value + '"] svg').click(); - await this.submitBtn.click(); + await this.click(this.page.locator(`[label="${value}"] svg`)); + await this.click(this.submitBtn); } // Image Cropper @@ -570,84 +554,65 @@ export class DataTypeUiHelper extends UiBaseLocators { } async clickCreateCropButton() { - await expect(this.createCropBtn).toBeVisible(); - await this.createCropBtn.click(); + await this.click(this.createCropBtn); } async clickEditCropButton() { - await expect(this.editCropBtn).toBeVisible(); - await this.editCropBtn.click(); + await this.click(this.editCropBtn); } async editCropByAlias(alias: string) { - await expect(this.page.locator('.crop').filter({has: this.page.getByText(alias)}).getByText('Edit')).toBeVisible(); - await this.page.locator('.crop').filter({has: this.page.getByText(alias)}).getByText('Edit').click(); + await this.click(this.page.locator('.crop').filter({has: this.page.getByText(alias)}).getByText('Edit')); } async removeCropByAlias(alias: string) { - await expect(this.page.locator('.crop').filter({has: this.page.getByText(alias)}).getByText('Remove')).toBeVisible(); - await this.page.locator('.crop').filter({has: this.page.getByText(alias)}).getByText('Remove').click(); + await this.click(this.page.locator('.crop').filter({has: this.page.getByText(alias)}).getByText('Remove')); } // Numeric async enterMinimumValue(value: string) { - await expect(this.minimumTxt).toBeVisible(); - await this.minimumTxt.clear(); - await this.minimumTxt.fill(value); + await this.enterText(this.minimumTxt, value); } async enterMaximumValue(value: string) { - await expect(this.maximumTxt).toBeVisible(); - await this.maximumTxt.clear(); - await this.maximumTxt.fill(value); + await this.enterText(this.maximumTxt, value); } async enterStepSizeValue(value: string) { - await expect(this.stepSizeTxt).toBeVisible(); - await this.stepSizeTxt.clear(); - await this.stepSizeTxt.fill(value); + await this.enterText(this.stepSizeTxt, value); } async clickAllowDecimalsToggle() { - await expect(this.allowDecimalsToggle).toBeVisible(); - await this.allowDecimalsToggle.click(); + await this.click(this.allowDecimalsToggle); } // Radiobox async removeOptionByName(name: string) { - await expect(this.page.locator("uui-button[label='Remove " + name + "'] svg")).toBeVisible(); - await this.page.locator("uui-button[label='Remove " + name + "'] svg").click(); - await this.confirmToDeleteBtn.click(); + await this.click(this.page.locator(`uui-button[label='Remove ${name}'] svg`)); + await this.click(this.confirmToDeleteBtn); } async enterOptionName(name: string) { - await expect(this.optionTxt.last()).toBeVisible(); + await this.waitForVisible(this.optionTxt.last()); await this.optionTxt.last().clear(); await this.optionTxt.last().fill(name); } async clickAddOptionButton() { - await expect(this.addOptionBtn).toBeVisible(); - await this.addOptionBtn.click(); + await this.click(this.addOptionBtn); } // Textarea - Textstring async enterMaximumAllowedCharactersValue(value: string) { - await expect(this.maximumAllowedCharsTxt).toBeVisible(); - await this.maximumAllowedCharsTxt.clear(); - await this.maximumAllowedCharsTxt.fill(value); + await this.enterText(this.maximumAllowedCharsTxt, value); } async enterNumberOfRowsValue(value: string) { - await expect(this.numberOfRowsTxt).toBeVisible(); - await this.numberOfRowsTxt.clear(); - await this.numberOfRowsTxt.fill(value); + await this.enterText(this.numberOfRowsTxt, value); } async enterMaxHeightValue(value: string) { - await expect(this.maxHeightTxt).toBeVisible(); - await this.maxHeightTxt.clear(); - await this.maxHeightTxt.fill(value); + await this.enterText(this.maxHeightTxt, value); } async enterMinHeightValue(value: string) { diff --git a/lib/helpers/DictionaryUiHelper.ts b/lib/helpers/DictionaryUiHelper.ts index 1ba597e1..5fff4084 100644 --- a/lib/helpers/DictionaryUiHelper.ts +++ b/lib/helpers/DictionaryUiHelper.ts @@ -36,14 +36,11 @@ export class DictionaryUiHelper extends UiBaseLocators { } async clickCreateDictionaryItemButton() { - await expect(this.createDictionaryItemBtn).toBeVisible(); - await this.createDictionaryItemBtn.click(); + await this.click(this.createDictionaryItemBtn); } async enterDictionaryName(name: string) { - await expect(this.dictionaryNameTxt).toBeVisible(); - await this.dictionaryNameTxt.clear(); - await this.dictionaryNameTxt.fill(name); + await this.enterText(this.dictionaryNameTxt, name); } async clickActionsMenuForDictionary(name: string) { @@ -51,17 +48,16 @@ export class DictionaryUiHelper extends UiBaseLocators { } async enterSearchKeywordAndPressEnter(keyword: string) { - await this.searchTxt.clear(); - await this.searchTxt.fill(keyword); + await this.enterText(this.searchTxt, keyword); await this.page.keyboard.press('Enter'); } async clickExportButton() { - await this.exportBtn.click(); + await this.click(this.exportBtn); } async clickImportButton() { - await this.importBtn.click(); + await this.click(this.importBtn); } async waitForDictionaryToBeCreated() { @@ -78,7 +74,7 @@ export class DictionaryUiHelper extends UiBaseLocators { async deleteDictionary() { await this.clickDeleteActionMenuOption(); - await this.confirmToDeleteBtn.click(); + await this.click(this.confirmToDeleteBtn); } async doesDictionaryListHaveText(text: string) { @@ -96,19 +92,18 @@ export class DictionaryUiHelper extends UiBaseLocators { // This function will export dictionary and return the file name async exportDictionary(includesDescendants: boolean) { if (includesDescendants) { - await this.includeDescendantsCheckbox.click(); + await this.click(this.includeDescendantsCheckbox); } const [downloadPromise] = await Promise.all([ this.page.waitForEvent('download'), - await this.exportModalBtn.click() + await this.click(this.exportModalBtn) ]); return downloadPromise.suggestedFilename(); } async importDictionary(filePath: string) { await this.importFileTxt.setInputFiles(filePath); - await expect(this.importModalBtn).toBeVisible(); - await this.importModalBtn.click(); + await this.click(this.importModalBtn); } async isSearchResultMessageDisplayEmpty(message: string) { diff --git a/lib/helpers/DocumentBlueprintUiHelper.ts b/lib/helpers/DocumentBlueprintUiHelper.ts index 6822b374..200a8876 100644 --- a/lib/helpers/DocumentBlueprintUiHelper.ts +++ b/lib/helpers/DocumentBlueprintUiHelper.ts @@ -41,7 +41,7 @@ export class DocumentBlueprintUiHelper extends UiBaseLocators{ async goToDocumentBlueprint(blueprintName: string) { await this.goToSection(ConstantHelper.sections.settings); await this.reloadDocumentBlueprintsTree(); - await this.page.getByLabel(blueprintName, {exact: true}).click(); + await this.click(this.page.getByLabel(blueprintName, {exact: true})); } async isDocumentBlueprintRootTreeItemVisible(blueprintName: string, isVisible: boolean = true, toReload: boolean = true){ @@ -52,19 +52,18 @@ export class DocumentBlueprintUiHelper extends UiBaseLocators{ } async clickCreateDocumentBlueprintButton() { - await this.createDocumentBlueprintBtn.click(); + await this.click(this.createDocumentBlueprintBtn); } - + async clickCreateNewDocumentBlueprintButton() { - await this.createNewDocumentBlueprintBtn.click(); + await this.click(this.createNewDocumentBlueprintBtn); } async enterDocumentBlueprintName(blueprintName: string) { - await expect(this.documentBlueprintNameTxt).toBeVisible(); - await this.documentBlueprintNameTxt.fill(blueprintName); + await this.enterText(this.documentBlueprintNameTxt, blueprintName); } async clickDeleteMenuButton() { - await this.deleteMenu.click(); + await this.click(this.deleteMenu); } } \ No newline at end of file diff --git a/lib/helpers/DocumentTypeUiHelper.ts b/lib/helpers/DocumentTypeUiHelper.ts index a5d1ebe8..d3a46067 100644 --- a/lib/helpers/DocumentTypeUiHelper.ts +++ b/lib/helpers/DocumentTypeUiHelper.ts @@ -50,38 +50,31 @@ export class DocumentTypeUiHelper extends UiBaseLocators { } async clickNewDocumentTypeButton() { - await expect(this.newDocumentTypeBtn).toBeVisible(); - await this.newDocumentTypeBtn.click(); + await this.click(this.newDocumentTypeBtn); } async clickSharedAcrossCulturesToggle() { - await expect(this.sharedAcrossCulturesToggle).toBeVisible(); - await this.sharedAcrossCulturesToggle.click(); + await this.click(this.sharedAcrossCulturesToggle); } async clickDocumentTypeSettingsTab() { - await expect(this.documentTypeSettingsTabBtn).toBeVisible(); - await this.documentTypeSettingsTabBtn.click(); + await this.click(this.documentTypeSettingsTabBtn); } async clickDocumentTypeTemplatesTab() { - await expect(this.documentTypeTemplatesTabBtn).toBeVisible(); - await this.documentTypeTemplatesTabBtn.click(); + await this.click(this.documentTypeTemplatesTabBtn); } async clickVaryBySegmentsButton() { - await expect(this.varyBySegmentsBtn).toBeVisible(); - await this.varyBySegmentsBtn.click(); + await this.click(this.varyBySegmentsBtn); } async clickVaryByCultureButton() { - await expect(this.varyByCultureBtn).toBeVisible(); - await this.varyByCultureBtn.click(); + await this.click(this.varyByCultureBtn); } async clickPreventCleanupButton() { - await expect(this.preventCleanupBtn).toBeVisible(); - await this.preventCleanupBtn.click(); + await this.click(this.preventCleanupBtn); } async goToDocumentType(documentTypeName: string) { @@ -102,42 +95,34 @@ export class DocumentTypeUiHelper extends UiBaseLocators { } async enterDocumentTypeName(documentTypeName: string) { - await expect(this.enterAName).toBeVisible(); - await this.enterAName.fill(documentTypeName); - await expect(this.enterAName).toHaveValue(documentTypeName); + await this.enterText(this.enterAName, documentTypeName, {verify: true}); } async clickCreateDocumentTypeButton() { - await expect(this.createDocumentTypeBtn).toBeVisible(); - await this.createDocumentTypeBtn.click(); + await this.click(this.createDocumentTypeBtn); } async clickCreateDocumentTypeWithTemplateButton() { - await expect(this.createDocumentTypeWithTemplateBtn).toBeVisible(); - await this.createDocumentTypeWithTemplateBtn.click(); + await this.click(this.createDocumentTypeWithTemplateBtn); } async clickCreateElementTypeButton() { - await expect(this.createElementTypeBtn).toBeVisible(); - await this.createElementTypeBtn.click(); + await this.click(this.createElementTypeBtn); } async clickCreateDocumentFolderButton() { - await expect(this.createDocumentFolderBtn).toBeVisible(); - await this.createDocumentFolderBtn.click(); + await this.click(this.createDocumentFolderBtn); } async isDocumentTreeItemVisible(name: string, isVisible = true) { - await expect(this.page.locator('umb-tree-item').locator('[label="' + name + '"]')).toBeVisible({visible: isVisible}); + await expect(this.page.locator('umb-tree-item').locator(`[label="${name}"]`)).toBeVisible({visible: isVisible}); } async clickSetAsDefaultButton() { - await expect(this.setAsDefaultBtn).toBeVisible(); - await this.setAsDefaultBtn.click(); + await this.click(this.setAsDefaultBtn); } async clickDocumentTypesMenu() { - await expect(this.documentTypesMenu).toBeVisible(); - await this.documentTypesMenu.click(); + await this.click(this.documentTypesMenu); } } \ No newline at end of file diff --git a/lib/helpers/LanguageUiHelper.ts b/lib/helpers/LanguageUiHelper.ts index 5b544a06..0e6b7f49 100644 --- a/lib/helpers/LanguageUiHelper.ts +++ b/lib/helpers/LanguageUiHelper.ts @@ -27,13 +27,11 @@ export class LanguageUiHelper extends UiBaseLocators { } async clickLanguageCreateButton() { - await expect(this.languageCreateBtn).toBeVisible(); - await this.languageCreateBtn.click(); + await this.click(this.languageCreateBtn); } async clickLanguagesMenu() { - await expect(this.languagesMenu).toBeVisible(); - await this.languagesMenu.click(); + await this.click(this.languagesMenu); } async goToLanguages() { @@ -48,20 +46,20 @@ export class LanguageUiHelper extends UiBaseLocators { async waitForLanguageToBeDeleted() { await this.page.waitForLoadState(); } - + async removeFallbackLanguageByIsoCode(isoCode: string) { - await this.page.locator('umb-entity-item-ref[id="' + isoCode + '"]').hover(); - await this.page.locator('umb-entity-item-ref[id="' + isoCode + '"]').getByLabel('Remove').click(); - await this.confirmToRemoveBtn.click(); + await this.hover(this.page.locator(`umb-entity-item-ref[id="${isoCode}"]`)); + await this.click(this.page.locator(`umb-entity-item-ref[id="${isoCode}"]`).getByLabel('Remove')); + await this.click(this.confirmToRemoveBtn); } async chooseLanguageByName(name: string) { - await this.languageDropdown.click(); - await this.page.locator('umb-input-culture-select').getByText(name, {exact: true}).click(); + await this.click(this.languageDropdown); + await this.click(this.page.locator('umb-input-culture-select').getByText(name, {exact: true})); } async clickLanguageByName(name: string) { - await this.languageTable.getByText(name, {exact: true}).click(); + await this.click(this.languageTable.getByText(name, {exact: true})); } async isLanguageNameVisible(name: string, isVisible = true) { @@ -69,19 +67,19 @@ export class LanguageUiHelper extends UiBaseLocators { } async switchDefaultLanguageOption() { - await this.defaultLanguageToggle.click(); + await this.click(this.defaultLanguageToggle); } async switchMandatoryLanguageOption() { - await this.mandatoryLanguageToggle.click(); + await this.click(this.mandatoryLanguageToggle); } async clickAddFallbackLanguageButton() { - await this.addFallbackLanguageBtn.click(); + await this.click(this.addFallbackLanguageBtn); } async clickRemoveLanguageByName(name: string) { - await this.page.locator('uui-table-row').filter({has: this.page.getByText(name, {exact: true})}).locator(this.deleteLanguageEntityAction).click({force: true}); + await this.click(this.page.locator('uui-table-row').filter({has: this.page.getByText(name, {exact: true})}).locator(this.deleteLanguageEntityAction), {force: true}); } async removeLanguageByName(name: string) { @@ -90,7 +88,7 @@ export class LanguageUiHelper extends UiBaseLocators { } async selectFallbackLanguageByName(name: string) { - await this.page.locator('umb-language-picker-modal').getByLabel(name).click(); + await this.click(this.page.locator('umb-language-picker-modal').getByLabel(name)); await this.clickSubmitButton(); } } \ No newline at end of file diff --git a/lib/helpers/LoginUiHelper.ts b/lib/helpers/LoginUiHelper.ts index fa2e5f19..33eb71e6 100644 --- a/lib/helpers/LoginUiHelper.ts +++ b/lib/helpers/LoginUiHelper.ts @@ -1,5 +1,6 @@ -import {Page, Locator, expect} from "@playwright/test"; +import {Page, Locator} from "@playwright/test"; import {UiBaseLocators} from "./UiBaseLocators"; +import {ConstantHelper} from "./ConstantHelper"; export class LoginUiHelper extends UiBaseLocators { private readonly emailTxt: Locator; @@ -10,21 +11,19 @@ export class LoginUiHelper extends UiBaseLocators { super(page); this.emailTxt = page.locator('[name="username"]'); this.passwordTxt = page.locator('[name="password"]'); - this.loginBtn = page.getByLabel('Login'); + this.loginBtn = page.getByLabel('Login'); } async enterEmail(email: string) { - await expect(this.emailTxt).toBeVisible({timeout: 20000}); - await this.emailTxt.clear(); - await this.emailTxt.fill(email); + await this.waitForVisible(this.emailTxt, ConstantHelper.timeout.navigation); + await this.enterText(this.emailTxt, email); } async enterPassword(password: string) { - await this.passwordTxt.clear(); - await this.passwordTxt.fill(password); + await this.enterText(this.passwordTxt, password); } async clickLoginButton() { - await this.loginBtn.click(); + await this.click(this.loginBtn); } } diff --git a/lib/helpers/MediaTypeUiHelper.ts b/lib/helpers/MediaTypeUiHelper.ts index b146a1fd..05f48971 100644 --- a/lib/helpers/MediaTypeUiHelper.ts +++ b/lib/helpers/MediaTypeUiHelper.ts @@ -30,19 +30,17 @@ export class MediaTypeUiHelper extends UiBaseLocators { } async clickNewMediaTypeButton() { - await this.newMediaTypeThreeDotsBtn.click(); + await this.click(this.newMediaTypeThreeDotsBtn); } async isMediaTypeTreeItemVisible(name: string, isVisible: boolean = true) { - { - const hasShowChildren = await this.mediaTypeTreeRoot.getAttribute('show-children') !== null; + const hasShowChildren = await this.mediaTypeTreeRoot.getAttribute('show-children') !== null; - if (!hasShowChildren) { - await this.mediaTypeTreeRoot.locator(this.caretBtn).first().click(); - } - - await this.isTreeItemVisible(name, isVisible); + if (!hasShowChildren) { + await this.click(this.mediaTypeTreeRoot.locator(this.caretBtn).first()); } + + await this.isTreeItemVisible(name, isVisible); } async waitForMediaTypeToBeCreated() { @@ -64,8 +62,7 @@ export class MediaTypeUiHelper extends UiBaseLocators { } async enterMediaTypeName(name: string) { - await this.enterAName.waitFor({state: 'visible'}); - await this.enterAName.fill(name); + await this.enterText(this.enterAName, name); } async enterDescriptionForPropertyEditorWithName(propertyEditorName: string, description: string) { @@ -73,12 +70,10 @@ export class MediaTypeUiHelper extends UiBaseLocators { } async clickMediaTypeButton() { - await expect(this.mediaTypeBtn).toBeVisible(); - await this.mediaTypeBtn.click(); + await this.click(this.mediaTypeBtn); } async clickMediaTypesMenu() { - await expect(this.mediaTypesMenu).toBeVisible(); - await this.mediaTypesMenu.click(); + await this.click(this.mediaTypesMenu); } } diff --git a/lib/helpers/MediaUiHelper.ts b/lib/helpers/MediaUiHelper.ts index 40e7eb0b..b183657c 100644 --- a/lib/helpers/MediaUiHelper.ts +++ b/lib/helpers/MediaUiHelper.ts @@ -1,5 +1,6 @@ import {UiBaseLocators} from "./UiBaseLocators"; import {expect, Locator, Page} from "@playwright/test"; +import {ConstantHelper} from "./ConstantHelper"; export class MediaUiHelper extends UiBaseLocators { private readonly createMediaItemBtn: Locator; @@ -50,18 +51,15 @@ export class MediaUiHelper extends UiBaseLocators { } async clickCreateMediaItemButton() { - await this.createMediaItemBtn.click(); + await this.click(this.createMediaItemBtn); } async enterMediaItemName(name: string) { - await expect(this.mediaNameTxt).toBeVisible(); - await this.mediaNameTxt.clear(); - await this.mediaNameTxt.fill(name); - await expect(this.mediaNameTxt).toHaveValue(name); + await this.enterText(this.mediaNameTxt, name, {verify: true}); } async clickMediaTypeWithNameButton(mediaTypeName: string) { - await this.page.getByLabel(mediaTypeName, {exact: true}).click(); + await this.click(this.page.getByLabel(mediaTypeName, {exact: true})); } async searchForMediaItemByName(name: string) { @@ -78,13 +76,13 @@ export class MediaUiHelper extends UiBaseLocators { } async clickTrashButton() { - await this.trashBtn.click(); + await this.click(this.trashBtn); } async restoreMediaItem(name: string) { await this.clickActionsMenuForName(name); - await this.restoreThreeDotsBtn.click(); - await this.page.waitForTimeout(1000); + await this.click(this.restoreThreeDotsBtn); + await this.page.waitForTimeout(ConstantHelper.wait.medium); await this.clickRestoreButton(); } @@ -111,40 +109,38 @@ export class MediaUiHelper extends UiBaseLocators { } async clickCreateMediaWithType(mediaTypeName: string) { - await expect(this.mediaCreateBtn).toBeVisible(); - await this.mediaCreateBtn.click(); + await this.click(this.mediaCreateBtn); await this.clickMediaTypeInPopoverByName(mediaTypeName); } async clickMediaTypeName(mediaTypeName: string) { - await this.documentTypeNode.filter({hasText: mediaTypeName}).click(); + await this.click(this.documentTypeNode.filter({hasText: mediaTypeName})); } async clickMediaTypeInPopoverByName(mediaTypeName: string) { - await this.mediaPopoverLayout.getByLabel(mediaTypeName).click(); + await this.click(this.mediaPopoverLayout.getByLabel(mediaTypeName)); } async clickEmptyRecycleBinButton() { - await this.recycleBinMenuItem.hover(); - await expect(this.emptyRecycleBinBtn).toBeVisible(); + await this.hover(this.recycleBinMenuItem); // Force click is needed - await this.emptyRecycleBinBtn.click({force: true}); + await this.click(this.emptyRecycleBinBtn, {force: true}); } async clickConfirmEmptyRecycleBinButton() { - await this.confirmEmptyRecycleBinBtn.click(); + await this.click(this.confirmEmptyRecycleBinBtn); } async clickCreateModalButton() { - await this.actionModalCreateBtn.click(); + await this.click(this.actionModalCreateBtn); } async clickMediaCaretButtonForName(name: string) { - await this.page.locator('umb-media-tree-item [label="' + name + '"]').locator('#caret-button').click(); + await this.click(this.page.locator(`umb-media-tree-item [label="${name}"]`).locator('#caret-button')); } async openMediaCaretButtonForName(name: string) { - const menuItem = this.page.locator('umb-media-tree-item [label="' + name + '"]') + const menuItem = this.page.locator(`umb-media-tree-item [label="${name}"]`); const isCaretButtonOpen = await menuItem.getAttribute('show-children'); if (isCaretButtonOpen === null) { @@ -183,22 +179,20 @@ export class MediaUiHelper extends UiBaseLocators { } async clickBulkTrashButton() { - await this.bulkTrashBtn.click(); + await this.click(this.bulkTrashBtn); } async clickBulkMoveToButton() { - await this.bulkMoveToBtn.click(); + await this.click(this.bulkMoveToBtn); } async clickModalTextByName(name: string) { - await this.sidebarModal.getByLabel(name, {exact: true}).click(); + await this.click(this.sidebarModal.getByLabel(name, {exact: true})); } async reloadMediaTree() { - await expect(this.mediaHeader).toBeVisible(); - await this.mediaHeader.click(); - await expect(this.mediaHeaderActionsMenu).toBeVisible(); - await this.mediaHeaderActionsMenu.click({force: true}); + await this.click(this.mediaHeader); + await this.click(this.mediaHeaderActionsMenu, {force: true}); await this.clickReloadChildrenActionMenuOption(); } @@ -207,7 +201,7 @@ export class MediaUiHelper extends UiBaseLocators { } async doesMediaItemInTreeHaveThumbnail(name: string, thumbnailIconName: string) { - const mediaThumbnailIconLocator = this.page.locator('umb-media-tree-item [label="' + name + '"]').locator('#icon-container #icon'); + const mediaThumbnailIconLocator = this.page.locator(`umb-media-tree-item [label="${name}"]`).locator('#icon-container #icon'); await expect(mediaThumbnailIconLocator).toHaveAttribute('name', thumbnailIconName); } @@ -216,11 +210,10 @@ export class MediaUiHelper extends UiBaseLocators { } async clickCaretButtonForMediaName(name: string) { - await this.mediaTreeItem.filter({hasText: name}).last().locator('#caret-button').last().click(); + await this.click(this.mediaTreeItem.filter({hasText: name}).last().locator('#caret-button').last()); } async goToMediaWithName(mediaName: string) { - await expect(this.mediaTreeItem.getByText(mediaName, {exact: true})).toBeVisible(); - await this.mediaTreeItem.getByText(mediaName, {exact: true}).click(); + await this.click(this.mediaTreeItem.getByText(mediaName, {exact: true})); } } \ No newline at end of file diff --git a/lib/helpers/MemberGroupUiHelper.ts b/lib/helpers/MemberGroupUiHelper.ts index 1feb80b1..ee977168 100644 --- a/lib/helpers/MemberGroupUiHelper.ts +++ b/lib/helpers/MemberGroupUiHelper.ts @@ -25,37 +25,34 @@ export class MemberGroupUiHelper extends UiBaseLocators { } async clickMemberGroupsTab() { - await expect(this.memberGroupsTab).toBeVisible(); - await this.page.waitForTimeout(500); - await this.memberGroupsTab.click(); + await this.waitForVisible(this.memberGroupsTab); + await this.page.waitForTimeout(ConstantHelper.wait.short); + await this.click(this.memberGroupsTab); await expect(this.activeMemberGroupsTab).toBeVisible(); } async clickMemberGroupCreateButton() { - await this.createMemberGroupBtn.click(); + await this.click(this.createMemberGroupBtn); } async clickMemberGroupsSidebarButton() { - await expect(this.memberGroupsSidebarBtn).toBeVisible(); - await this.memberGroupsSidebarBtn.click(); + await this.click(this.memberGroupsSidebarBtn); } async enterMemberGroupName(name: string) { - await expect(this.memberGroupNameTxt).toBeVisible(); - await this.memberGroupNameTxt.clear(); - await this.memberGroupNameTxt.fill(name); + await this.enterText(this.memberGroupNameTxt, name); } async clickMemberGroupLinkByName(memberGroupName: string) { - await this.page.getByRole('link', {name: memberGroupName}).click(); + await this.click(this.page.getByRole('link', {name: memberGroupName})); } async isMemberGroupNameVisible(memberGroupName: string, isVisible: boolean = true) { - return expect(this.memberGroupView.filter({hasText: memberGroupName})).toBeVisible({visible: isVisible, timeout: 500}); + return expect(this.memberGroupView.filter({hasText: memberGroupName})).toBeVisible({visible: isVisible, timeout: ConstantHelper.wait.short}); } async clickMemberGroupsMenu() { - await this.memberGroupsMenu.click(); + await this.click(this.memberGroupsMenu); } async waitForMemberGroupToBeCreated() { diff --git a/lib/helpers/MemberTypeUiHelper.ts b/lib/helpers/MemberTypeUiHelper.ts index dc18ac18..f73c2648 100644 --- a/lib/helpers/MemberTypeUiHelper.ts +++ b/lib/helpers/MemberTypeUiHelper.ts @@ -25,12 +25,11 @@ export class MemberTypeUiHelper extends UiBaseLocators { async goToMemberType(memberTypeName: string) { await this.clickRootFolderCaretButton(); - await this.page.getByLabel(memberTypeName).click(); + await this.click(this.page.getByLabel(memberTypeName)); } async enterMemberTypeName(name: string) { - await this.memberTypeNameTxt.clear(); - await this.memberTypeNameTxt.fill(name); + await this.enterText(this.memberTypeNameTxt, name); } async enterDescriptionForPropertyEditorWithName(propertyEditorName: string, description: string) { diff --git a/lib/helpers/MemberUiHelper.ts b/lib/helpers/MemberUiHelper.ts index d3725dfc..88424554 100644 --- a/lib/helpers/MemberUiHelper.ts +++ b/lib/helpers/MemberUiHelper.ts @@ -49,75 +49,60 @@ export class MemberUiHelper extends UiBaseLocators { } async clickMembersTab() { - await this.membersTab.click(); + await this.click(this.membersTab); } async clickDetailsTab() { - await expect(this.detailsTab).toBeVisible(); - await this.detailsTab.click(); + await this.click(this.detailsTab); } async clickMemberLinkByName(memberName: string) { - await this.page.getByRole('link', {name: memberName}).click(); + await this.click(this.page.getByRole('link', {name: memberName})); } async isMemberWithNameVisible(memberName: string, isVisible: boolean = true) { await expect(this.memberTableCollectionRow.getByText(memberName, {exact: true})).toBeVisible({visible: isVisible}); } - + async clickMembersSidebarButton() { - await expect(this.membersSidebarBtn).toBeVisible(); - await this.membersSidebarBtn.click(); + await this.click(this.membersSidebarBtn); } - + async enterSearchKeyword(keyword: string) { - await expect(this.searchTxt).toBeVisible(); - await this.searchTxt.clear(); - await this.searchTxt.fill(keyword); + await this.enterText(this.searchTxt, keyword); } async enterMemberName(name: string) { - await expect(this.memberNameTxt).toBeVisible(); - await this.memberNameTxt.clear(); - await this.memberNameTxt.fill(name); + await this.enterText(this.memberNameTxt, name); } async enterComments(comment: string) { - await expect(this.commentsTxt).toBeVisible(); - await this.commentsTxt.clear(); - await this.commentsTxt.fill(comment); + await this.enterText(this.commentsTxt, comment); } async enterUsername(username: string) { - await expect(this.usernameTxt).toBeVisible(); - await this.usernameTxt.clear(); - await this.usernameTxt.fill(username); + await this.enterText(this.usernameTxt, username); } async enterEmail(email: string) { - await expect(this.emailTxt).toBeVisible(); - await this.emailTxt.clear(); - await this.emailTxt.fill(email); + await this.enterText(this.emailTxt, email); } async enterPassword(password: string) { - await this.passwordTxt.clear(); - await this.passwordTxt.fill(password); + await this.enterText(this.passwordTxt, password); } async enterConfirmPassword(password: string) { - await this.confirmPasswordTxt.clear(); - await this.confirmPasswordTxt.fill(password); + await this.enterText(this.confirmPasswordTxt, password); } async enterConfirmNewPassword(password: string) { - await this.confirmNewPasswordTxt.clear(); - await this.confirmNewPasswordTxt.fill(password); + await this.enterText(this.confirmNewPasswordTxt, password); } async chooseMemberGroup(memberGroupName: string) { await this.clickChooseButton(); - await this.page.getByText(memberGroupName, {exact: true}).click(); + await this.click(this.page.getByText(memberGroupName, {exact: true})); await this.clickChooseContainerButton(); } @@ -126,38 +111,37 @@ export class MemberUiHelper extends UiBaseLocators { } async clickApprovedToggle() { - await this.approvedToggle.click(); + await this.click(this.approvedToggle); } async clickLockedOutToggle() { - await this.lockedOutToggle.click(); + await this.click(this.lockedOutToggle); } async clickTwoFactorAuthenticationToggle() { - await this.twoFactorAuthenticationToggle.click(); + await this.click(this.twoFactorAuthenticationToggle); } async clickChangePasswordButton() { - await this.changePasswordBtn.click(); + await this.click(this.changePasswordBtn); } async clickRemoveMemberGroupByName(memberGroupName: string) { - await this.page.locator('[name="' + memberGroupName + '"]').getByLabel('Remove').click(); + await this.click(this.page.locator(`[name="${memberGroupName}"]`).getByLabel('Remove')); } async enterNewPassword(password: string) { - await this.newPasswordTxt.clear(); - await this.newPasswordTxt.fill(password); + await this.enterText(this.newPasswordTxt, password); } async clickMembersMenu() { - await this.membersMenu.click(); + await this.click(this.membersMenu); } - + async waitForMemberToBeCreated(){ await this.page.waitForLoadState(); } - + async waitForMemberToBeDeleted() { await this.page.waitForLoadState(); } @@ -168,12 +152,10 @@ export class MemberUiHelper extends UiBaseLocators { } async clickInfoTab() { - await expect(this.infoTab).toBeVisible(); - await this.infoTab.click(); + await this.click(this.infoTab); } async clickCreateMembersButton() { - await expect(this.membersCreateBtn).toBeVisible(); - await this.membersCreateBtn.click(); + await this.click(this.membersCreateBtn); } } diff --git a/lib/helpers/PackageUiHelper.ts b/lib/helpers/PackageUiHelper.ts index f09add38..31c87509 100644 --- a/lib/helpers/PackageUiHelper.ts +++ b/lib/helpers/PackageUiHelper.ts @@ -1,6 +1,7 @@ import {expect, Locator, Page,} from "@playwright/test" import {UiBaseLocators} from "./UiBaseLocators"; import {umbracoConfig} from "../../umbraco.config"; +import {ConstantHelper} from "./ConstantHelper"; export class PackageUiHelper extends UiBaseLocators { private readonly createdTabBtn: Locator; @@ -60,24 +61,21 @@ export class PackageUiHelper extends UiBaseLocators { } async clickCreatedTab() { - await this.page.waitForTimeout(500); - await expect(this.createdTabBtn).toBeVisible(); - await this.createdTabBtn.click(); - await this.page.waitForTimeout(500); + await this.page.waitForTimeout(ConstantHelper.wait.short); + await this.click(this.createdTabBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickInstalledTab() { - await expect(this.installedTabBtn).toBeVisible(); - await this.installedTabBtn.click(); + await this.click(this.installedTabBtn); } async clickPackagesTab() { - await expect(this.packagesTabBtn).toBeVisible(); - await this.packagesTabBtn.click(); + await this.click(this.packagesTabBtn); } async clickChooseBtn() { - await this.chooseModalBtn.click(); + await this.click(this.chooseModalBtn); } async isMarketPlaceIFrameVisible(isVisible = true) { @@ -85,13 +83,12 @@ export class PackageUiHelper extends UiBaseLocators { } async clickCreatePackageButton() { - await this.createPackageBtn.click(); + await this.click(this.createPackageBtn); } async enterPackageName(packageName: string) { - await this.packageNameTxt.clear(); - await this.packageNameTxt.fill(packageName); - await this.page.waitForTimeout(500); + await this.enterText(this.packageNameTxt, packageName); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async isPackageNameVisible(packageName: string, isVisible = true) { @@ -99,69 +96,67 @@ export class PackageUiHelper extends UiBaseLocators { } async clickExistingPackageName(packageName: string) { - await this.page.getByRole('button', {name: packageName}).click(); - await this.page.waitForTimeout(500); + await this.click(this.page.getByRole('button', {name: packageName})); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickDeleteButtonForPackageName(packageName: string) { const deletePackageWithNameLocator = this.page.locator('uui-ref-node-package', {hasText: packageName}).getByLabel('Delete'); - await expect(deletePackageWithNameLocator).toBeVisible(); - await deletePackageWithNameLocator.click(); + await this.click(deletePackageWithNameLocator); } async clickSaveChangesToPackageButton() { - await this.saveChangesToPackageBtn.click(); + await this.click(this.saveChangesToPackageBtn); } async clickAddContentToPackageButton() { - await this.addContentToPackageBtn.click(); + await this.click(this.addContentToPackageBtn); } async clickAddMediaToPackageButton() { - await expect(this.addMediaToPackageBtn).toBeVisible(); - await this.addMediaToPackageBtn.click(); + await this.click(this.addMediaToPackageBtn); } async clickAddDocumentTypeToPackageButton() { - await this.addDocumentTypeToPackageBtn.click(); + await this.click(this.addDocumentTypeToPackageBtn); } async clickAddMediaTypeToPackageButton() { - await this.addMediaTypeToPackageBtn.click(); + await this.click(this.addMediaTypeToPackageBtn); } async clickAddLanguageToPackageButton() { - await this.addLanguageToPackageBtn.click(); + await this.click(this.addLanguageToPackageBtn); } async clickAddDictionaryToPackageButton() { - await this.addDictionaryToPackageBtn.click(); + await this.click(this.addDictionaryToPackageBtn); } async clickAddDataTypesToPackageButton() { - await this.addDataTypesToPackageBtn.click(); + await this.click(this.addDataTypesToPackageBtn); } async clickAddTemplatesToPackageButton() { - await this.addTemplatesToPackagesBtn.click(); + await this.click(this.addTemplatesToPackagesBtn); } async clickAddPartialViewToPackageButton() { - await this.addPartialViewToPackageBtn.click(); + await this.click(this.addPartialViewToPackageBtn); } async clickAddScriptToPackageButton() { - await this.addScriptToPackageBtn.click(); + await this.click(this.addScriptToPackageBtn); } async clickAddStylesheetToPackageButton() { - await this.addStylesheetToPackageBtn.click(); + await this.click(this.addStylesheetToPackageBtn); } // Downloads the package and converts it to a string async downloadPackage(packageId: string) { - const responsePromise = this.page.waitForResponse(umbracoConfig.environment.baseUrl + '/umbraco/management/api/v1/package/created/' + packageId + '/download'); - await this.downloadPackageBtn.click(); + const responsePromise = this.page.waitForResponse(`${umbracoConfig.environment.baseUrl}/umbraco/management/api/v1/package/created/${packageId}/download`); + await this.click(this.downloadPackageBtn); const response = await responsePromise; const body = await response.body(); return body.toString().trim(); diff --git a/lib/helpers/PartialViewUiHelper.ts b/lib/helpers/PartialViewUiHelper.ts index fbd115b3..0b9a2b6a 100644 --- a/lib/helpers/PartialViewUiHelper.ts +++ b/lib/helpers/PartialViewUiHelper.ts @@ -1,5 +1,6 @@ import {Page, Locator, expect} from "@playwright/test" import {UiBaseLocators} from "./UiBaseLocators"; +import {ConstantHelper} from "./ConstantHelper"; export class PartialViewUiHelper extends UiBaseLocators { private readonly newEmptyPartialViewBtn: Locator; @@ -44,18 +45,15 @@ export class PartialViewUiHelper extends UiBaseLocators { } async clickNewEmptyPartialViewButton() { - await this.newEmptyPartialViewBtn.click(); + await this.click(this.newEmptyPartialViewBtn); } async clickNewPartialViewFromSnippetButton() { - await this.newPartialViewFromSnippetBtn.click(); + await this.click(this.newPartialViewFromSnippetBtn); } async enterPartialViewName(partialViewName: string) { - await expect(this.enterAName).toBeVisible(); - await this.enterAName.click(); - await this.enterAName.clear(); - await this.enterAName.fill(partialViewName); + await this.enterText(this.enterAName, partialViewName); await expect(this.enterAName).toHaveValue(partialViewName); } @@ -64,18 +62,18 @@ export class PartialViewUiHelper extends UiBaseLocators { await this.waitUntilPartialViewLoaderIsNoLongerVisible(); await this.enterMonacoEditorValue(partialViewContent); // We need this wait, to be sure that the partial view content is loaded. - await this.page.waitForTimeout(200); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async openPartialViewAtRoot(partialViewName: string) { await this.reloadPartialViewTree(); - await this.page.locator('uui-menu-item[label="' + partialViewName +'"]').click(); - await expect(this.enterAName).toBeVisible(); + await this.click(this.page.locator(`uui-menu-item[label="${partialViewName}"]`)); + await this.waitForVisible(this.enterAName); } async createPartialViewFolder(folderName: string) { await this.clickCreateOptionsActionMenuOption(); - await this.newFolderThreeDots.click(); + await this.click(this.newFolderThreeDots); await this.enterFolderName(folderName); await this.clickConfirmCreateFolderButton(); } diff --git a/lib/helpers/RelationTypeUiHelper.ts b/lib/helpers/RelationTypeUiHelper.ts index fe7de7c7..19e6aeb9 100644 --- a/lib/helpers/RelationTypeUiHelper.ts +++ b/lib/helpers/RelationTypeUiHelper.ts @@ -38,8 +38,7 @@ export class RelationTypeUiHelper extends UiBaseLocators{ } async goToRelationTypeWithName(name: string) { - await expect(this.relationTypeCollection.getByText(name)).toBeVisible(); - await this.relationTypeCollection.getByText(name).click(); + await this.click(this.relationTypeCollection.getByText(name)); await this.waitUntilUiLoaderIsNoLongerVisible(); } @@ -53,16 +52,15 @@ export class RelationTypeUiHelper extends UiBaseLocators{ async openRelationTypeByNameAtRoot(relationTypeName: string) { await this.clickRootFolderCaretButton(); - await this.page.getByLabel(relationTypeName).click(); + await this.click(this.page.getByLabel(relationTypeName)); } async enterRelationTypeName(name: string) { - await this.relationTypeNameTxt.clear(); - await this.relationTypeNameTxt.fill(name); + await this.enterText(this.relationTypeNameTxt, name); } async clickParentToChildRadioButton() { - await this.parentToChildRadioBtn.click(); + await this.click(this.parentToChildRadioBtn); } async doesParentTypeContainValue(value: string) { @@ -86,11 +84,11 @@ export class RelationTypeUiHelper extends UiBaseLocators{ } async clickBidirectionalRadioButton() { - await this.bidirectionalRadioBtn.click(); + await this.click(this.bidirectionalRadioBtn); } async clickIsDependencyToggle() { - await this.isDependencyToggle.click(); + await this.click(this.isDependencyToggle); } async selectParentOption(option: string) { diff --git a/lib/helpers/ScriptUiHelper.ts b/lib/helpers/ScriptUiHelper.ts index 425f9233..f86e1ddc 100644 --- a/lib/helpers/ScriptUiHelper.ts +++ b/lib/helpers/ScriptUiHelper.ts @@ -22,8 +22,7 @@ export class ScriptUiHelper extends UiBaseLocators{ async createScriptFolder(folderName: string) { await this.clickCreateOptionsActionMenuOption(); - await expect(this.newFolderThreeDots).toBeVisible(); - await this.newFolderThreeDots.click(); + await this.click(this.newFolderThreeDots); await this.enterFolderName(folderName); await this.clickConfirmCreateFolderButton(); } @@ -37,8 +36,7 @@ export class ScriptUiHelper extends UiBaseLocators{ } async clickNewJavascriptFileButton() { - await expect(this.newJavascriptFileBtn).toBeVisible(); - await this.newJavascriptFileBtn.click(); + await this.click(this.newJavascriptFileBtn); } async waitForScriptToBeCreated() { @@ -57,12 +55,11 @@ export class ScriptUiHelper extends UiBaseLocators{ async goToScript(scriptName: string) { await this.goToSection(ConstantHelper.sections.settings); await this.reloadScriptTree(); - await this.page.getByLabel(scriptName, {exact: true}).click(); + await this.click(this.page.getByLabel(scriptName, {exact: true})); } async enterScriptName(scriptContent: string) { - await expect(this.enterAName).toBeVisible(); - await this.enterAName.fill(scriptContent); + await this.enterText(this.enterAName, scriptContent); } async enterScriptContent(scriptContent: string) { @@ -71,7 +68,7 @@ export class ScriptUiHelper extends UiBaseLocators{ async openScriptAtRoot(scriptName: string) { await this.reloadScriptTree(); - await this.page.getByLabel(scriptName, {exact: true}).click(); + await this.click(this.page.getByLabel(scriptName, {exact: true})); } async reloadScriptTree() { diff --git a/lib/helpers/StylesheetUiHelper.ts b/lib/helpers/StylesheetUiHelper.ts index 188f6cd0..82109c53 100644 --- a/lib/helpers/StylesheetUiHelper.ts +++ b/lib/helpers/StylesheetUiHelper.ts @@ -38,13 +38,11 @@ export class StylesheetUiHelper extends UiBaseLocators{ } async clickNewStylesheetButton() { - await expect(this.newStylesheetBtn).toBeVisible(); - await this.newStylesheetBtn.click(); - } - + await this.click(this.newStylesheetBtn); + } + async clickNewStylesheetFolderButton() { - await expect(this.newStylesheetFolderBtn).toBeVisible(); - await this.newStylesheetFolderBtn.click(); + await this.click(this.newStylesheetFolderBtn); } async waitForStylesheetToBeCreated() { @@ -60,9 +58,7 @@ export class StylesheetUiHelper extends UiBaseLocators{ } async enterStylesheetName(stylesheetName: string) { - await expect(this.stylesheetNameTxt).toBeVisible(); - await this.stylesheetNameTxt.clear(); - await this.stylesheetNameTxt.fill(stylesheetName); + await this.enterText(this.stylesheetNameTxt, stylesheetName); } async enterStylesheetContent(stylesheetContent: string) { @@ -71,7 +67,7 @@ export class StylesheetUiHelper extends UiBaseLocators{ async openStylesheetByNameAtRoot(stylesheetName: string) { await this.reloadStylesheetTree(); - await this.page.getByLabel(stylesheetName, {exact: true}).click(); + await this.click(this.page.getByLabel(stylesheetName, {exact: true})); } async reloadStylesheetTree() { @@ -88,6 +84,6 @@ export class StylesheetUiHelper extends UiBaseLocators{ async goToStylesheet(stylesheetName: string) { await this.goToSection(ConstantHelper.sections.settings); await this.reloadStylesheetTree(); - await this.page.getByLabel(stylesheetName, {exact: true}).click(); + await this.click(this.page.getByLabel(stylesheetName, {exact: true})); } } \ No newline at end of file diff --git a/lib/helpers/TemplateUiHelper.ts b/lib/helpers/TemplateUiHelper.ts index 3b6cdd61..538d8e5c 100644 --- a/lib/helpers/TemplateUiHelper.ts +++ b/lib/helpers/TemplateUiHelper.ts @@ -46,53 +46,48 @@ export class TemplateUiHelper extends UiBaseLocators { await this.goToSection(ConstantHelper.sections.settings); await this.reloadTemplateTree(); if (childTemplateName === '') { - await this.page.getByLabel(templateName, {exact: true}).click(); + await this.click(this.page.getByLabel(templateName, {exact: true})); await expect(this.enterAName).toHaveValue(templateName); } else { await this.openCaretButtonForName(templateName); - await this.page.getByLabel(childTemplateName , {exact: true}).click(); + await this.click(this.page.getByLabel(childTemplateName, {exact: true})); await expect(this.enterAName).toHaveValue(childTemplateName); } - await this.page.waitForTimeout(1000); + await this.page.waitForTimeout(ConstantHelper.wait.medium); } async clickSectionsButton() { - await expect(this.sectionsBtn).toBeVisible(); - await this.sectionsBtn.click(); + await this.click(this.sectionsBtn); } async clickChangeMasterTemplateButton() { - await expect(this.changeMasterTemplateBtn).toBeVisible(); - await this.changeMasterTemplateBtn.click(); + await this.click(this.changeMasterTemplateBtn); } async enterTemplateName(templateName: string) { - await expect(this.enterAName).toBeVisible(); - await this.enterAName.clear(); - await this.enterAName.fill(templateName); + await this.enterText(this.enterAName, templateName); } async enterTemplateContent(templateContent: string) { // We need this wait, to be sure that the template content is loaded. - await this.page.waitForTimeout(200); + await this.page.waitForTimeout(ConstantHelper.wait.minimal); await this.enterMonacoEditorValue(templateContent); } async isMasterTemplateNameVisible(templateName: string, isVisible: boolean = true) { - await expect(this.page.getByLabel('Master template: ' + templateName)).toBeVisible({visible: isVisible}); + await expect(this.page.getByLabel(`Master template: ${templateName}`)).toBeVisible({visible: isVisible}); } async clickRemoveMasterTemplateButton() { - await expect(this.removeMasterTemplateBtn).toBeVisible(); - await this.removeMasterTemplateBtn.click(); + await this.click(this.removeMasterTemplateBtn); } async insertSection(sectionType: string, sectionName: string = '') { await this.clickSectionsButton(); - await expect(this.submitBtn).toBeVisible(); - await this.page.locator('[label="' + sectionType + '"]').click(); + await this.waitForVisible(this.submitBtn); + await this.click(this.page.locator(`[label="${sectionType}"]`)); if (sectionName !== '') { - await expect(this.sectionNameTxt).toBeVisible(); + await this.waitForVisible(this.sectionNameTxt); await this.sectionNameTxt.fill(sectionName); } await this.clickSubmitButton(); diff --git a/lib/helpers/UserGroupUiHelper.ts b/lib/helpers/UserGroupUiHelper.ts index 5faa46d6..5881016a 100644 --- a/lib/helpers/UserGroupUiHelper.ts +++ b/lib/helpers/UserGroupUiHelper.ts @@ -1,5 +1,6 @@ import {expect, Locator, Page} from "@playwright/test" import {UiBaseLocators} from "./UiBaseLocators"; +import {ConstantHelper} from "./ConstantHelper"; export class UserGroupUiHelper extends UiBaseLocators { private readonly userGroupsBtn: Locator; @@ -44,37 +45,30 @@ export class UserGroupUiHelper extends UiBaseLocators { } async clickUserGroupsButton() { - await expect(this.userGroupsBtn).toBeVisible(); - await this.userGroupsBtn.click(); - await this.page.waitForTimeout(500); + await this.click(this.userGroupsBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async enterUserGroupName(name: string) { - await expect(this.enterAName).toBeVisible(); - await this.enterAName.clear(); - await this.enterAName.fill(name); + await this.enterText(this.enterAName, name); } async addLanguageToUserGroup(languageName: string) { - await expect(this.chooseLanguageBtn).toBeVisible(); - await this.chooseLanguageBtn.click(); + await this.click(this.chooseLanguageBtn); await this.clickLabelWithName(languageName, true); await this.clickSubmitButton(); } async clickAllowAccessToAllLanguages() { - await expect(this.allowAccessToAllLanguagesBtn).toBeVisible(); - await this.allowAccessToAllLanguagesBtn.click(); + await this.click(this.allowAccessToAllLanguagesBtn); } async clickAllowAccessToAllDocuments() { - await expect(this.allowAccessToAllDocumentsBtn).toBeVisible(); - await this.allowAccessToAllDocumentsBtn.click(); + await this.click(this.allowAccessToAllDocumentsBtn); } async clickAllowAccessToAllMedia() { - await expect(this.allowAccessToAllMediaBtn).toBeVisible(); - await this.allowAccessToAllMediaBtn.click(); + await this.click(this.allowAccessToAllMediaBtn); } async waitForUserGroupToBeCreated() { @@ -90,13 +84,11 @@ export class UserGroupUiHelper extends UiBaseLocators { } async clickCreateUserGroupButton() { - await expect(this.userGroupCreateBtn).toBeVisible(); - await this.userGroupCreateBtn.click(); + await this.click(this.userGroupCreateBtn); } async clickRemoveLanguageFromUserGroup(languageName: string) { - await expect(this.entityItem.filter({hasText: languageName}).getByLabel('Remove')).toBeVisible(); - await this.entityItem.filter({hasText: languageName}).getByLabel('Remove').click(); + await this.click(this.entityItem.filter({hasText: languageName}).getByLabel('Remove')); } async isUserGroupWithNameVisible(name: string, isVisible = true) { @@ -104,20 +96,19 @@ export class UserGroupUiHelper extends UiBaseLocators { } async clickUserGroupWithName(name: string) { - await expect(this.page.getByRole('link', {name: name})).toBeVisible(); - await this.page.getByRole('link', {name: name}).click(); - await this.page.waitForTimeout(200); + await this.click(this.page.getByRole('link', {name: name})); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickPermissionsByName(permissionName: string[]) { for (let i = 0; i < permissionName.length; i++) { - await this.permissionVerbBtn.getByText(permissionName[i], {exact: true}).click(); + await this.click(this.permissionVerbBtn.getByText(permissionName[i], {exact: true})); } } async clickGranularPermissionsByName(permissionName: string[]) { for (let i = 0; i < permissionName.length; i++) { - await this.granularPermissionsModal.getByText(permissionName[i], {exact: true}).click(); + await this.click(this.granularPermissionsModal.getByText(permissionName[i], {exact: true})); } } @@ -136,8 +127,7 @@ export class UserGroupUiHelper extends UiBaseLocators { } async clickChooseSectionButton() { - await expect(this.chooseSectionBtn).toBeVisible(); - await this.chooseSectionBtn.click(); + await this.click(this.chooseSectionBtn); } async doesUserGroupTableHaveSection(userGroupName: string, sectionName: string, hasSection = true) { @@ -150,20 +140,16 @@ export class UserGroupUiHelper extends UiBaseLocators { } async clickRemoveSectionFromUserGroup(sectionName: string) { - await expect(this.section.filter({hasText: sectionName}).getByLabel('Remove')).toBeVisible(); - await this.section.filter({hasText: sectionName}).getByLabel('Remove').click(); + await this.click(this.section.filter({hasText: sectionName}).getByLabel('Remove')); } async clickRemoveContentStartNodeFromUserGroup(contentStartNodeName: string) { - await expect(this.contentStartNode.filter({hasText: contentStartNodeName}).getByLabel('Remove')).toBeVisible(); - await this.contentStartNode.filter({hasText: contentStartNodeName}).getByLabel('Remove').click(); + await this.click(this.contentStartNode.filter({hasText: contentStartNodeName}).getByLabel('Remove')); } async clickRemoveMediaStartNodeFromUserGroup(mediaStartNodeName: string) { - const removeMediaStartNodeWithNameLocator = this.mediaStartNode.filter({hasText: mediaStartNodeName}).getByLabel('Remove'); - await expect(removeMediaStartNodeWithNameLocator).toBeVisible(); // Force click is needed - await removeMediaStartNodeWithNameLocator.click({force: true}); + await this.click(this.mediaStartNode.filter({hasText: mediaStartNodeName}).getByLabel('Remove'), {force: true}); } async doesUserGroupHavePermissionEnabled(permissionName: string[]) { @@ -173,18 +159,15 @@ export class UserGroupUiHelper extends UiBaseLocators { } async clickGranularPermissionWithName(permissionName: string) { - await expect(this.granularPermission.getByText(permissionName)).toBeVisible(); - await this.granularPermission.getByText(permissionName).click(); + await this.click(this.granularPermission.getByText(permissionName)); } async clickAddGranularPermission() { - await expect(this.addGranularPermissionBtn).toBeVisible(); - await this.addGranularPermissionBtn.click(); + await this.click(this.addGranularPermissionBtn); } async clickRemoveGranularPermissionWithName(permissionName: string) { - await expect(this.granularPermission.filter({hasText: permissionName}).getByLabel('Remove')).toBeVisible(); - await this.granularPermission.filter({hasText: permissionName}).getByLabel('Remove').click(); + await this.click(this.granularPermission.filter({hasText: permissionName}).getByLabel('Remove')); } async doesSettingHaveValue(headline: string, settings) { diff --git a/lib/helpers/UserUiHelper.ts b/lib/helpers/UserUiHelper.ts index ecd0d4b1..3b7e07c1 100644 --- a/lib/helpers/UserUiHelper.ts +++ b/lib/helpers/UserUiHelper.ts @@ -72,21 +72,20 @@ export class UserUiHelper extends UiBaseLocators { } async clickUsersButton() { - await expect(this.usersBtn).toBeVisible(); - await this.usersBtn.click(); + await this.click(this.usersBtn); } async clickCreateUserButton() { - await this.createUserBtn.click(); - await this.page.waitForTimeout(500); + await this.click(this.createUserBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async enterNameOfTheUser(name: string) { - await this.nameOfTheUserTxt.fill(name); + await this.enterText(this.nameOfTheUserTxt, name); } async enterUserEmail(email: string) { - await this.userEmailTxt.fill(email); + await this.enterText(this.userEmailTxt, email); } async waitForUserToBeCreated() { @@ -102,31 +101,30 @@ export class UserUiHelper extends UiBaseLocators { } async clickAddUserGroupsButton() { - await this.addUserGroupsBtn.click(); + await this.click(this.addUserGroupsBtn); // This wait is necessary to avoid the click on the user group button to be ignored - await this.page.waitForTimeout(200); + await this.page.waitForTimeout(ConstantHelper.wait.minimal); } async clickChooseUserGroupsButton() { - await this.chooseUserGroupsBtn.click(); + await this.click(this.chooseUserGroupsBtn); } async clickOpenUserGroupsButton() { - await this.openUserGroupsBtn.click(); + await this.click(this.openUserGroupsBtn); } async enterUpdatedNameOfUser(name: string) { - await this.updatedNameOfTheUserTxt.fill(name); + await this.enterText(this.updatedNameOfTheUserTxt, name); } async clickUserWithName(name: string) { const userNameLocator = this.page.locator('#open-part').getByText(name, {exact: true}); - await expect(userNameLocator).toBeVisible(); - await userNameLocator.click(); + await this.click(userNameLocator); } async clickChangePasswordButton() { - await this.changePasswordBtn.click(); + await this.click(this.changePasswordBtn); } async updatePassword(newPassword: string) { @@ -139,15 +137,15 @@ export class UserUiHelper extends UiBaseLocators { } async clickChangePhotoButton() { - await this.changePhotoBtn.click(); + await this.click(this.changePhotoBtn); } async clickRemoveButtonForUserGroupWithName(userGroupName: string) { - await this.page.locator('umb-user-group-ref', {hasText: userGroupName}).locator('[label="Remove"]').click(); + await this.click(this.page.locator('umb-user-group-ref', {hasText: userGroupName}).locator('[label="Remove"]')); } async clickRemovePhotoButton() { - await this.removePhotoBtn.click(); + await this.click(this.removePhotoBtn); } async changePhotoWithFileChooser(filePath: string) { @@ -165,7 +163,7 @@ export class UserUiHelper extends UiBaseLocators { let userCount = 0; while (true) { - await this.page.waitForTimeout(1000); + await this.page.waitForTimeout(ConstantHelper.wait.medium); userCount += await this.userSectionCard.count(); // Check if pagination exists and next button is enabled @@ -183,18 +181,18 @@ export class UserUiHelper extends UiBaseLocators { await this.clickNextPaginationButton(); } - + // If we actually navigated through the pagination, we should go back if (amount > 50) { const firstPage = this.firstPaginationBtn; const isFirstPageEnabled = await firstPage.isEnabled(); if (isFirstPageEnabled) { - await firstPage.click(); + await this.click(firstPage); } - await this.page.waitForTimeout(1000); + await this.page.waitForTimeout(ConstantHelper.wait.medium); } - + return expect(userCount).toBe(amount); } @@ -203,13 +201,13 @@ export class UserUiHelper extends UiBaseLocators { } async filterByStatusName(statusName: string) { - await this.statusBtn.click(); - await this.page.locator('label').filter({hasText: statusName}).click(); + await this.click(this.statusBtn); + await this.click(this.page.locator('label').filter({hasText: statusName})); } async filterByGroupName(groupName: string) { - await this.groupBtn.click(); - await this.page.locator('label').filter({hasText: groupName}).click(); + await this.click(this.groupBtn); + await this.click(this.page.locator('label').filter({hasText: groupName})); } async isPasswordUpdatedForUserWithId(userId: string) { @@ -220,7 +218,7 @@ export class UserUiHelper extends UiBaseLocators { } async clickChooseContainerButton() { - await this.chooseContainerBtn.click(); + await this.click(this.chooseContainerBtn); } async selectUserLanguage(language: string) { @@ -228,20 +226,20 @@ export class UserUiHelper extends UiBaseLocators { } async clickRemoveButtonForContentNodeWithName(name: string) { - await this.entityItem.filter({has: this.page.locator('[name="' + name + '"]')}).hover(); - await this.entityItem.filter({has: this.page.locator('[name="' + name + '"]')}).getByRole('button', { name: 'Remove' }).click({force: true}); + await this.hover(this.entityItem.filter({has: this.page.locator(`[name="${name}"]`)})); + await this.click(this.entityItem.filter({has: this.page.locator(`[name="${name}"]`)}).getByRole('button', {name: 'Remove'}), {force: true}); } async clickRemoveButtonForMediaNodeWithName(name: string) { - await this.mediaInput.locator('[name="' + name + '"]').locator('[label="Remove"]').click(); + await this.click(this.mediaInput.locator(`[name="${name}"]`).locator('[label="Remove"]')); } async clickAllowAccessToAllDocumentsToggle() { - await this.allowAccessToAllDocumentsToggle.click(); + await this.click(this.allowAccessToAllDocumentsToggle); } async clickAllowAccessToAllMediaToggle() { - await this.allowAccessToAllMediaToggle.click(); + await this.click(this.allowAccessToAllMediaToggle); } async isUserDisabledTextVisible() { @@ -253,10 +251,9 @@ export class UserUiHelper extends UiBaseLocators { } async orderByNewestUser() { - await expect(this.orderByBtn).toBeVisible(); // Force click is needed - await this.orderByBtn.click({force: true}); - await this.orderByNewestBtn.click(); + await this.click(this.orderByBtn, {force: true}); + await this.click(this.orderByNewestBtn); } async isUserWithNameTheFirstUserInList(name: string) { @@ -264,15 +261,15 @@ export class UserUiHelper extends UiBaseLocators { } async doesUserHaveAccessToContentNode(name: string) { - return await expect(this.documentStartNode.locator('[name="' + name + '"]')).toBeVisible(); + return await expect(this.documentStartNode.locator(`[name="${name}"]`)).toBeVisible(); } async doesUserHaveAccessToMediaNode(name: string) { - return await expect(this.mediaStartNode.locator('[name="' + name + '"]')).toBeVisible(); + return await expect(this.mediaStartNode.locator(`[name="${name}"]`)).toBeVisible(); } async clickUsersMenu() { - await this.usersMenu.click(); + await this.click(this.usersMenu); } async goToUsers() { @@ -288,14 +285,14 @@ export class UserUiHelper extends UiBaseLocators { } async clickUserButton() { - await this.userBtn.click(); + await this.click(this.userBtn); } - + async isGoToProfileButtonVisible(isVisible: boolean = true) { await expect(this.goToProfileBtn).toBeVisible({visible: isVisible}); } async clickAPIUserButton() { - await this.apiUserBtn.click(); + await this.click(this.apiUserBtn); } } \ No newline at end of file diff --git a/lib/helpers/WebhookUiHelper.ts b/lib/helpers/WebhookUiHelper.ts index f46c3d18..b25e94ce 100644 --- a/lib/helpers/WebhookUiHelper.ts +++ b/lib/helpers/WebhookUiHelper.ts @@ -41,62 +41,47 @@ export class WebhookUiHelper extends UiBaseLocators { } async clickWebhookCreateButton() { - await expect(this.webhookCreateBtn).toBeVisible(); - await this.webhookCreateBtn.click(); + await this.click(this.webhookCreateBtn); } async enterWebhookName(name: string) { - await expect(this.webhookNameTxt).toBeVisible(); - await this.webhookNameTxt.clear(); - await this.webhookNameTxt.fill(name) + await this.enterText(this.webhookNameTxt, name); } async enterUrl(url: string) { - await expect(this.urlTxt).toBeVisible(); - await this.urlTxt.clear(); - await this.urlTxt.fill(url); + await this.enterText(this.urlTxt, url); } async clickChooseEventButton() { - await expect(this.chooseEventBtn).toBeVisible(); - await this.chooseEventBtn.click(); + await this.click(this.chooseEventBtn); } async clickChooseContentTypeButton() { - await expect(this.chooseContentTypeBtn).toBeVisible(); - await this.chooseContentTypeBtn.click(); + await this.click(this.chooseContentTypeBtn); } async clickEnabledToggleButton() { - await expect(this.enabledToggle).toBeVisible(); - await this.enabledToggle.click(); + await this.click(this.enabledToggle); } async clickAddHeadersButton() { - await expect(this.addHeadersBtn).toBeVisible(); - await this.addHeadersBtn.click(); + await this.click(this.addHeadersBtn); } async enterHeaderName(name: string) { - await expect(this.headerNameTxt).toBeVisible(); - await this.headerNameTxt.clear(); - await this.headerNameTxt.fill(name); + await this.enterText(this.headerNameTxt, name); } async enterHeaderValue(value: string) { - await expect(this.headerValueTxt).toBeVisible(); - await this.headerValueTxt.clear(); - await this.headerValueTxt.fill(value); + await this.enterText(this.headerValueTxt, value); } async clickDeleteWebhookWithName(name: string) { const deleteLocator = this.page.locator('uui-table-row').filter({has: this.page.getByText(name, {exact: true})}).locator(this.deleteWebhookEntityAction); - await expect(deleteLocator).toBeVisible(); - await deleteLocator.click(); + await this.click(deleteLocator); } async clickHeaderRemoveButton() { - await expect(this.headerRemoveBtn).toBeVisible(); - await this.headerRemoveBtn.click(); + await this.click(this.headerRemoveBtn); } } \ No newline at end of file From a49544e498f716b40954a65a0fbb948d9dd97a18 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 16:07:28 +0700 Subject: [PATCH 09/34] Refactor remaining UI helpers to use BasePage methods and time constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace direct Playwright calls with BasePage wrapper methods (click, enterText, hover, waitForVisible) across all remaining UI helpers. Standardizes hardcoded timeout values to use ConstantHelper constants and uses template literals for string concatenation. Files refactored: - DataTypeUiHelper.ts - FormsUiHelper.ts - ContentUiHelper.ts - LogViewerUiHelper.ts - PublishedStatusUiHelper.ts - RedirectManagementUiHelper.ts - HealthCheckUiHelper.ts - ExamineManagementUiHelper.ts - ProfilingUiHelper.ts - ModelsBuilderUiHelper.ts - TelemetryDataUiHelper.ts - WelcomeDashboardUiHelper.ts - InstallUiHelper.ts - CurrentUserProfileUiHelper.ts - MediaTypeUiHelper.ts - WebhookUiHelper.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- lib/helpers/ContentUiHelper.ts | 4 +- lib/helpers/CurrentUserProfileUiHelper.ts | 2 +- lib/helpers/DataTypeUiHelper.ts | 358 +++++++----------- lib/helpers/ExamineManagementUiHelper.ts | 6 +- lib/helpers/FormsUiHelper.ts | 358 ++++++++---------- lib/helpers/HealthCheckUiHelper.ts | 6 +- lib/helpers/LogViewerUiHelper.ts | 41 +- lib/helpers/MediaTypeUiHelper.ts | 2 +- lib/helpers/ModelsBuilderUiHelper.ts | 2 +- lib/helpers/ProfilingUiHelper.ts | 6 +- lib/helpers/PublishedStatusUiHelper.ts | 12 +- lib/helpers/RedirectManagementUiHelper.ts | 16 +- lib/helpers/TelemetryDataUiHelper.ts | 2 +- lib/helpers/WebhookUiHelper.ts | 2 +- lib/helpers/WelcomeDashboardUiHelper.ts | 2 +- .../InstallUiHelper.ts | 16 +- 16 files changed, 343 insertions(+), 492 deletions(-) diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index ee4bd060..14c26e78 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -454,8 +454,8 @@ export class ContentUiHelper extends UiBaseLocators { } async enterTextArea(value: string) { - await expect(this.textAreaTxt).toBeVisible(); - await this.page.waitForTimeout(200); + await this.waitForVisible(this.textAreaTxt); + await this.page.waitForTimeout(ConstantHelper.wait.minimal); await this.textAreaTxt.clear(); await this.textAreaTxt.fill(value); } diff --git a/lib/helpers/CurrentUserProfileUiHelper.ts b/lib/helpers/CurrentUserProfileUiHelper.ts index 1139306c..bdd47cd9 100644 --- a/lib/helpers/CurrentUserProfileUiHelper.ts +++ b/lib/helpers/CurrentUserProfileUiHelper.ts @@ -1,4 +1,4 @@ -import {expect, Locator, Page} from "@playwright/test" +import {Locator, Page} from "@playwright/test" import {UiBaseLocators} from "./UiBaseLocators"; export class CurrentUserProfileUiHelper extends UiBaseLocators { diff --git a/lib/helpers/DataTypeUiHelper.ts b/lib/helpers/DataTypeUiHelper.ts index aa7fdd67..788c340a 100644 --- a/lib/helpers/DataTypeUiHelper.ts +++ b/lib/helpers/DataTypeUiHelper.ts @@ -453,8 +453,7 @@ export class DataTypeUiHelper extends UiBaseLocators { async addColor(value: string) { await this.click(this.addColorBtn); - await this.colorValueTxt.clear(); - await this.colorValueTxt.fill(value); + await this.enterText(this.colorValueTxt, value); } // Label @@ -468,14 +467,12 @@ export class DataTypeUiHelper extends UiBaseLocators { } async enterDateFormatValue(value: string) { - await this.dateFormatTxt.clear(); - await this.dateFormatTxt.fill(value); + await this.enterText(this.dateFormatTxt, value); } // List View async enterPageSizeValue(value: string) { - await this.pageSizeTxt.clear(); - await this.pageSizeTxt.fill(value); + await this.enterText(this.pageSizeTxt, value); } async chooseOrderDirection(isAscending: boolean) { @@ -512,8 +509,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async enterWorkspaceViewName(name: string) { - await this.workspaceViewName.clear(); - await this.workspaceViewName.fill(name); + await this.enterText(this.workspaceViewName, name); } async clickShowContentWorkspaceViewFirstToggle() { @@ -540,17 +536,10 @@ export class DataTypeUiHelper extends UiBaseLocators { // Image Cropper async enterCropValues(label: string, alias: string, width: string, height: string) { - await expect(this.labelTxt).toBeVisible(); - await this.labelTxt.clear(); - await this.labelTxt.fill(label); - await expect(this.aliasTxt).toBeVisible(); - await this.aliasTxt.clear(); - await this.aliasTxt.fill(alias); - await expect(this.widthTxt).toBeVisible(); - await this.widthTxt.clear(); - await this.widthTxt.fill(width); - await this.heightTxt.clear(); - await this.heightTxt.fill(height); + await this.enterText(this.labelTxt, label); + await this.enterText(this.aliasTxt, alias); + await this.enterText(this.widthTxt, width); + await this.enterText(this.heightTxt, height); } async clickCreateCropButton() { @@ -593,9 +582,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async enterOptionName(name: string) { - await this.waitForVisible(this.optionTxt.last()); - await this.optionTxt.last().clear(); - await this.optionTxt.last().fill(name); + await this.enterText(this.optionTxt.last(), name); } async clickAddOptionButton() { @@ -616,38 +603,34 @@ export class DataTypeUiHelper extends UiBaseLocators { } async enterMinHeightValue(value: string) { - await this.minHeightTxt.clear(); - await this.minHeightTxt.fill(value); + await this.enterText(this.minHeightTxt, value); } // Upload async enterAcceptedFileExtensions(value: string) { - await this.acceptedFileExtensionsTxt.last().clear(); - await this.acceptedFileExtensionsTxt.last().fill(value); + await this.enterText(this.acceptedFileExtensionsTxt.last(), value); } async removeAcceptedFileExtensionsByValue(value: string) { - await this.page.locator("uui-button[label='Remove " + value + "'] svg").click(); - await this.confirmToDeleteBtn.click(); + await this.click(this.page.locator(`uui-button[label='Remove ${value}'] svg`)); + await this.click(this.confirmToDeleteBtn); } async clickAddAcceptedFileExtensionsButton() { - await this.addAcceptedFileExtensionsBtn.click(); + await this.click(this.addAcceptedFileExtensionsBtn); } // Multi URL Picker async enterMinimumNumberOfItemsValue(value: string) { - await this.minimumNumberOfItemsTxt.clear(); - await this.minimumNumberOfItemsTxt.fill(value); + await this.enterText(this.minimumNumberOfItemsTxt, value); } async enterMaximumNumberOfItemsValue(value: string) { - await this.maximumNumberOfItemsTxt.clear(); - await this.maximumNumberOfItemsTxt.fill(value); + await this.enterText(this.maximumNumberOfItemsTxt, value); } async clickIgnoreUserStartNodesToggle() { - await this.ignoreUserStartNodesToggle.click(); + await this.click(this.ignoreUserStartNodesToggle); } async chooseOverlaySizeByValue(value: string) { @@ -655,81 +638,75 @@ export class DataTypeUiHelper extends UiBaseLocators { } async clickHideAnchorQueryStringInputToggle() { - await this.hideAnchorQueryStringInputToggle.click(); + await this.click(this.hideAnchorQueryStringInputToggle); } // Media Picker async clickPickMultipleItemsToggle() { - await this.pickMultipleItemsToggle.click(); + await this.click(this.pickMultipleItemsToggle); } async clickEnableFocalPointToggle() { - await expect(this.enableFocalPointToggle).toBeVisible(); - await this.enableFocalPointToggle.click(); + await this.click(this.enableFocalPointToggle); } async enterAmountValue(lowValue: string, highValue: string) { - await this.amountLowValueTxt.clear(); - await this.amountLowValueTxt.fill(lowValue); - await this.amountHighValueTxt.clear(); - await this.amountHighValueTxt.fill(highValue); + await this.enterText(this.amountLowValueTxt, lowValue); + await this.enterText(this.amountHighValueTxt, highValue); } async addAcceptedType(mediaTypeName: string) { - await this.chooseAcceptedTypesBtn.click(); + await this.click(this.chooseAcceptedTypesBtn); await this.clickTextButtonWithName(mediaTypeName); - await this.chooseModalBtn.click(); + await this.click(this.chooseModalBtn); } async removeAcceptedType(mediaTypeName: string) { - await this.page.locator('uui-ref-node-document-type[name="' + mediaTypeName + '"]').getByLabel('Remove').click(); - await this.confirmToRemoveBtn.click(); + await this.click(this.page.locator(`uui-ref-node-document-type[name="${mediaTypeName}"]`).getByLabel('Remove')); + await this.click(this.confirmToRemoveBtn); } async removeMediaStartNode(mediaName: string) { - await this.page.locator('uui-card-media[name="' + mediaName + '"]').locator('[label="Remove"]').click(); - await this.confirmToRemoveBtn.click(); + await this.click(this.page.locator(`uui-card-media[name="${mediaName}"]`).locator('[label="Remove"]')); + await this.click(this.confirmToRemoveBtn); } async clickChooseStartNodeButton() { - await this.chooseStartNodeBtn.click(); + await this.click(this.chooseStartNodeBtn); } // Richtext Editor async clickToolbarOptionByValue(values) { for (var index in values) { - await this.toolbarCheckboxes.filter({has: this.page.getByLabel(values[index])}).locator('#ticker svg').click(); + await this.click(this.toolbarCheckboxes.filter({has: this.page.getByLabel(values[index])}).locator('#ticker svg')); } } async addStylesheet(stylesheetName: string) { - await this.addStylesheetBtn.click(); - await this.page.getByLabel(stylesheetName).click(); - await this.chooseModalBtn.click(); + await this.click(this.addStylesheetBtn); + await this.click(this.page.getByLabel(stylesheetName)); + await this.click(this.chooseModalBtn); } async enterDimensionsValue(width: string, height: string) { - await this.dimensionsWidthTxt.clear(); - await this.dimensionsWidthTxt.fill(width); - await this.dimensionsHeightTxt.clear(); - await this.dimensionsHeightTxt.fill(height); + await this.enterText(this.dimensionsWidthTxt, width); + await this.enterText(this.dimensionsHeightTxt, height); } async enterMaximumSizeForImages(value: string) { - await this.maxImageSizeTxt.clear(); - await this.maxImageSizeTxt.fill(value); + await this.enterText(this.maxImageSizeTxt, value); } async clickHideLabelToggle() { - await this.hideLabelToggle.click(); + await this.click(this.hideLabelToggle); } async clickInlineRadioButton() { - await this.inlineRadioBtn.click(); + await this.click(this.inlineRadioBtn); } async clickChooseWithPlusButton() { - await this.chooseWithPlusBtn.click(); + await this.click(this.chooseWithPlusBtn); } async addImageUploadFolder(mediaFolderName: string) { @@ -739,8 +716,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async clickAddWithPlusButton() { - await expect(this.addWithPlusBtn).toBeVisible(); - await this.addWithPlusBtn.click(); + await this.click(this.addWithPlusBtn); } async addAvailableBlocks(blockName: string) { @@ -752,86 +728,69 @@ export class DataTypeUiHelper extends UiBaseLocators { // Tags async enterDefineTagGroupValue(value: string) { - await expect(this.defineTagGroupTxt).toBeVisible(); - await this.defineTagGroupTxt.clear(); - await this.defineTagGroupTxt.fill(value); + await this.enterText(this.defineTagGroupTxt, value); } async selectStorageTypeOption(option: string) { - await expect(this.storageTypeDropDownBox).toBeVisible(); + await this.waitForVisible(this.storageTypeDropDownBox); await this.storageTypeDropDownBox.selectOption({label: option}); } // Content Picker async clickShowOpenButtonToggle() { - await expect(this.showOpenButtonToggle).toBeVisible(); - await this.showOpenButtonToggle.click(); + await this.click(this.showOpenButtonToggle); } async removeContentStartNode(contentName: string) { - const startNodeLocator = this.entityItem.filter({has: this.page.locator('[name="' + contentName + '"]')}); - await startNodeLocator.hover(); - await startNodeLocator.getByLabel('Remove').click(); + const startNodeLocator = this.entityItem.filter({has: this.page.locator(`[name="${contentName}"]`)}); + await this.hover(startNodeLocator); + await this.click(startNodeLocator.getByLabel('Remove')); await this.clickConfirmRemoveButton(); } // Dropdown async clickEnableMultipleChoiceToggle() { - await expect(this.enableMultipleChoiceToggle).toBeVisible(); - await this.enableMultipleChoiceToggle.click(); + await this.click(this.enableMultipleChoiceToggle); } async clickAddOptionsButton() { - await expect(this.addOptionsBtn).toBeVisible(); - await this.addOptionsBtn.click(); + await this.click(this.addOptionsBtn); } // True/false async clickPresetValueToggle() { - await expect(this.presetValueToggle).toBeVisible(); - await this.presetValueToggle.click(); + await this.click(this.presetValueToggle); } async clickShowToggleLabelsToggle() { - await expect(this.showToggleLabelsToggle).toBeVisible(); - await this.showToggleLabelsToggle.click(); + await this.click(this.showToggleLabelsToggle); } async enterLabelOnValue(value: string) { - await expect(this.labelOnTxt).toBeVisible(); - await this.labelOnTxt.clear(); - await this.labelOnTxt.fill(value); + await this.enterText(this.labelOnTxt, value); } async enterLabelOffValue(value: string) { - await expect(this.labelOffTxt).toBeVisible(); - await this.labelOffTxt.clear(); - await this.labelOffTxt.fill(value); + await this.enterText(this.labelOffTxt, value); } // Block List Editor async clickAddBlockButton(index: number = 0) { - await expect(this.addBlockBtn.nth(index)).toBeVisible(); - await this.addBlockBtn.nth(index).click(); + await this.click(this.addBlockBtn.nth(index)); } async clickRemoveBlockWithName(name: string) { const blockWithNameLocator = this.page.locator('umb-block-type-card', {hasText: name}); - await expect(blockWithNameLocator).toBeVisible(); - // The force click is necessary. - await blockWithNameLocator.getByLabel('Remove block').click({force: true}); + // The force click is necessary. + await this.click(blockWithNameLocator.getByLabel('Remove block'), {force: true}); } async enterMinAmount(value: string) { - await expect(this.minAmountTxt).toBeVisible() - await this.minAmountTxt.clear(); - await this.minAmountTxt.fill(value); + await this.enterText(this.minAmountTxt, value); } async enterMaxAmount(value: string) { - await expect(this.maxAmountTxt).toBeVisible() - await this.maxAmountTxt.clear(); - await this.maxAmountTxt.fill(value); + await this.enterText(this.maxAmountTxt, value); } async doesAmountContainErrorMessageWithText(errorMessage: string) { @@ -839,29 +798,23 @@ export class DataTypeUiHelper extends UiBaseLocators { } async clickSingleBlockMode() { - await expect(this.singleBlockModeBtn).toBeVisible() - await this.singleBlockModeBtn.click(); + await this.click(this.singleBlockModeBtn); } async clickLiveEditingMode() { - await expect(this.liveEditingModeBtn).toBeVisible(); - await this.liveEditingModeBtn.click(); + await this.click(this.liveEditingModeBtn); } async clickInlineEditingMode() { - await expect(this.inlineEditingModeBtn).toBeVisible(); - await this.inlineEditingModeBtn.click(); + await this.click(this.inlineEditingModeBtn); } async enterPropertyEditorWidth(width: string) { - await expect(this.propertyEditorWidthTxt).toBeVisible(); - await this.propertyEditorWidthTxt.clear(); - await this.propertyEditorWidthTxt.fill(width); + await this.enterText(this.propertyEditorWidthTxt, width); } async goToBlockWithName(name: string) { - await expect(this.page.getByRole('link', {name: name})).toBeVisible(); - await this.page.getByRole('link', {name: name}).click(); + await this.click(this.page.getByRole('link', {name: name})); } async enterBlockLabelText(label: string) { @@ -870,65 +823,53 @@ export class DataTypeUiHelper extends UiBaseLocators { } async removeBlockLabelText() { - await expect(this.labelTextTxt).toBeVisible(); + await this.waitForVisible(this.labelTextTxt); await this.labelTextTxt.clear(); } async clickAllowInRootForBlock() { - await expect(this.allowBlockAtRootToggle).toBeVisible(); - await this.allowBlockAtRootToggle.click(); + await this.click(this.allowBlockAtRootToggle); } async clickAllowInAreasForBlock() { - await expect(this.allowInAreasToggle).toBeVisible(); - await this.allowInAreasToggle.click(); + await this.click(this.allowInAreasToggle); } async updateBlockOverlaySize(size: string) { - await expect(this.overlaySizeOption).toBeVisible(); + await this.waitForVisible(this.overlaySizeOption); await this.overlaySizeOption.selectOption(size); } async addBlockContentModel(elementName: string) { - await expect(this.chooseContentModelBtn).toBeVisible(); - await this.chooseContentModelBtn.click(); + await this.click(this.chooseContentModelBtn); await this.clickButtonWithName(elementName); await this.clickChooseButton(); } async addBlockSettingsModel(elementName: string) { - await expect(this.chooseSettingsModelBtn).toBeVisible(); - await this.chooseSettingsModelBtn.click(); + await this.click(this.chooseSettingsModelBtn); await this.clickModalMenuItemWithName(elementName); await this.clickChooseModalButton(); } async removeBlockContentModel() { - await expect(this.contentModelNode).toBeVisible(); - await this.contentModelNode.hover(); - await expect(this.removeExactContentModelNodeBtn).toBeVisible(); - await this.removeExactContentModelNodeBtn.click(); + await this.hover(this.contentModelNode); + await this.click(this.removeExactContentModelNodeBtn); } async removeBlockSettingsModel() { - await expect(this.settingsModelNode).toBeVisible(); - await this.settingsModelNode.hover(); - await expect(this.removeExactSettingsModelNodeBtn).toBeVisible(); - await this.removeExactSettingsModelNodeBtn.click(); + await this.hover(this.settingsModelNode); + await this.click(this.removeExactSettingsModelNodeBtn); } async openBlockContentModel() { - await expect(this.contentModelNode).toBeVisible(); - await this.contentModelNode.hover(); - await expect(this.openBtn).toBeVisible(); - await this.openBtn.click(); + await this.hover(this.contentModelNode); + await this.click(this.openBtn); } async openBlockSettingsModel() { - await expect(this.settingsModelNode).toBeVisible(); - await this.settingsModelNode.hover(); - await expect(this.openBtn).toBeVisible(); - await this.openBtn.click(); + await this.hover(this.settingsModelNode); + await this.click(this.openBtn); } async isElementWorkspaceOpenInBlock(elementTypeName: string) { @@ -936,40 +877,31 @@ export class DataTypeUiHelper extends UiBaseLocators { } async selectBlockBackgroundColor(color: string) { - await expect(this.backgroundColorBtn).toBeVisible(); - await this.backgroundColorBtn.click(); - await this.backgroundColorTxt.clear(); - await this.backgroundColorTxt.fill(color); + await this.click(this.backgroundColorBtn); + await this.enterText(this.backgroundColorTxt, color); } async selectBlockIconColor(color: string) { - await expect(this.iconColorBtn).toBeVisible(); - await this.iconColorBtn.click(); - await this.iconColorTxt.clear(); - await this.iconColorTxt.fill(color); + await this.click(this.iconColorBtn); + await this.enterText(this.iconColorTxt, color); } async clickExpandChildItemsForMediaButton() { - await expect(this.expandChildItemsForMediaBtn).toBeVisible(); - await this.expandChildItemsForMediaBtn.click(); + await this.click(this.expandChildItemsForMediaBtn); } async clickRemoveCustomStylesheetWithName(name: string) { - await expect(this.customStylesheetLabel.locator('[name="' + name + '"]')).toBeVisible(); - await this.customStylesheetLabel.locator('[name="' + name + '"]').click(); - await expect(this.stylesheetRemoveBtn).toBeVisible(); - await this.stylesheetRemoveBtn.click(); + await this.click(this.customStylesheetLabel.locator(`[name="${name}"]`)); + await this.click(this.stylesheetRemoveBtn); await this.clickConfirmRemoveButton(); } async clickBlockGridHideContentEditorButton() { - await expect(this.hideContentEditorBlockGridBtn).toBeVisible(); - await this.hideContentEditorBlockGridBtn.click(); + await this.click(this.hideContentEditorBlockGridBtn); } async chooseBlockCustomStylesheetWithName(name: string) { - await expect(this.chooseCustomStylesheetBtn).toBeVisible(); - await this.chooseCustomStylesheetBtn.click(); + await this.click(this.chooseCustomStylesheetBtn); await this.openCaretButtonForName('wwwroot'); await this.openCaretButtonForName('css'); await this.clickLabelWithName(name, true); @@ -978,39 +910,33 @@ export class DataTypeUiHelper extends UiBaseLocators { async chooseBlockThumbnailWithPath(mediaPath: string) { const mediaItems = mediaPath.split('/media/')[1].split('/'); - await expect(this.chooseThumbnailAlias).toBeVisible(); - await this.chooseThumbnailAlias.click(); + await this.click(this.chooseThumbnailAlias); await this.openCaretButtonForName('wwwroot', true); await this.clickExpandChildItemsForMediaButton(); for (let i = 0; i < mediaItems.length; i++) { if (i === mediaItems.length - 1) { await this.clickLabelWithName(mediaItems[i], true); } else { - await this.sidebarModal.locator('uui-menu-item[label="' + mediaItems[i] + '"] #caret-button').click(); + await this.click(this.sidebarModal.locator(`uui-menu-item[label="${mediaItems[i]}"] #caret-button`)); } } await this.clickChooseModalButton(); } async clickBlockListHideContentEditorButton() { - await expect(this.hideContentEditorBlockListBtn).toBeVisible(); - await this.hideContentEditorBlockListBtn.click(); + await this.click(this.hideContentEditorBlockListBtn); } async enterEditorWidth(value: string) { - await expect(this.editorWidthTxt).toBeVisible(); - await this.editorWidthTxt.clear(); - await this.editorWidthTxt.fill(value); + await this.enterText(this.editorWidthTxt, value); } async enterCreateButtonLabel(value: string) { - await expect(this.createButtonLabelTxt).toBeVisible(); - await this.createButtonLabelTxt.clear(); - await this.createButtonLabelTxt.fill(value); + await this.enterText(this.createButtonLabelTxt, value); } async enterGridColumns(value: number) { - await expect(this.gridColumnsTxt).toBeVisible(); + await this.waitForVisible(this.gridColumnsTxt); await this.gridColumnsTxt.clear(); if (value === undefined) { return; @@ -1019,24 +945,21 @@ export class DataTypeUiHelper extends UiBaseLocators { } async clickShowResizeOptions() { - await expect(this.showResizeOptionsBtn).toBeVisible(); - await this.showResizeOptionsBtn.click(); + await this.click(this.showResizeOptionsBtn); } async clickAvailableColumnSpans(columnSpans: number[]) { for (let index in columnSpans) { - await expect(this.columnSpanOptions.getByLabel(columnSpans[index].toString(), {exact: true})).toBeVisible(); - await this.columnSpanOptions.getByLabel(columnSpans[index].toString(), {exact: true}).click(); + await this.click(this.columnSpanOptions.getByLabel(columnSpans[index].toString(), {exact: true})); } } async goToBlockAreasTab() { - await expect(this.areasTabBtn).toBeVisible(); - await this.areasTabBtn.click(); + await this.click(this.areasTabBtn); } async enterMinRowSpan(value: number) { - await expect(this.availableRowSpansLowValueTxt).toBeVisible(); + await this.waitForVisible(this.availableRowSpansLowValueTxt); await this.availableRowSpansLowValueTxt.clear(); if (value === undefined) { return; @@ -1045,7 +968,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async enterMaxRowSpan(value: number) { - await expect(this.availableRowSpansHighValueTxt).toBeVisible(); + await this.waitForVisible(this.availableRowSpansHighValueTxt); await this.availableRowSpansHighValueTxt.clear(); if (value === undefined) { return; @@ -1054,7 +977,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async enterGridColumnsForArea(value: number) { - await expect(this.areaGridColumnsTxt).toBeVisible(); + await this.waitForVisible(this.areaGridColumnsTxt); await this.areaGridColumnsTxt.clear(); if (value === undefined) { return; @@ -1063,37 +986,33 @@ export class DataTypeUiHelper extends UiBaseLocators { } async addAreaButton() { - await expect(this.addAreaBtn).toBeVisible(); - await this.addAreaBtn.click(); + await this.click(this.addAreaBtn); } async goToAreaByAlias(alias: string) { const editAreaWithAliasLocator = this.blockAreaConfig.filter({hasText: alias}).getByLabel('edit'); - await expect(editAreaWithAliasLocator).toBeVisible(); - await editAreaWithAliasLocator.click({force: true}); + // Force click is needed + await this.click(editAreaWithAliasLocator, {force: true}); } async clickRemoveAreaByAlias(alias: string) { const removeAreaWithAliasLocator = this.blockAreaConfig.filter({hasText: alias}).getByLabel('delete'); - await expect(removeAreaWithAliasLocator).toBeVisible(); - await removeAreaWithAliasLocator.click({force: true}); + // Force click is needed + await this.click(removeAreaWithAliasLocator, {force: true}); await this.clickConfirmToDeleteButton(); } async enterAreaAlias(alias: string) { - await expect(this.aliasAliasTxt).toBeVisible(); - await this.aliasAliasTxt.clear(); - await this.aliasAliasTxt.fill(alias); + await this.enterText(this.aliasAliasTxt, alias); } async clickAreaSubmitButton() { - await expect(this.blockGridAreaWorkspaceSubmitBtn).toBeVisible(); - await this.blockGridAreaWorkspaceSubmitBtn.click(); - await this.page.waitForTimeout(500); + await this.click(this.blockGridAreaWorkspaceSubmitBtn); + await this.page.waitForTimeout(ConstantHelper.wait.short); } async enterCreateButtonLabelInArea(value: string) { - await expect(this.createLabelTxt.nth(1)).toBeVisible(); + await this.waitForVisible(this.createLabelTxt.nth(1)); await this.createLabelTxt.nth(1).clear(); if (value === undefined) { return; @@ -1102,7 +1021,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async enterMinAllowedInArea(value: number) { - await expect(this.minAllowedTxt).toBeVisible(); + await this.waitForVisible(this.minAllowedTxt); await this.minAllowedTxt.clear(); if (value === undefined) { return; @@ -1111,7 +1030,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async enterMaxAllowedInArea(value: number) { - await expect(this.maxAllowedTxt).toBeVisible(); + await this.waitForVisible(this.maxAllowedTxt); await this.maxAllowedTxt.clear(); if (value === undefined) { return; @@ -1120,13 +1039,11 @@ export class DataTypeUiHelper extends UiBaseLocators { } async clickAddSpecifiedAllowanceButton() { - await expect(this.addSpecifiedAllowanceBtn).toBeVisible(); - await this.addSpecifiedAllowanceBtn.click(); + await this.click(this.addSpecifiedAllowanceBtn); } async goToBlockAdvancedTab() { - await expect(this.advancedTabBtn).toBeVisible(); - await this.advancedTabBtn.click(); + await this.click(this.advancedTabBtn); } async getLinkWithName(name: string) { @@ -1141,43 +1058,37 @@ export class DataTypeUiHelper extends UiBaseLocators { async clickRemoveStylesheetButton(stylesheetName: string) { const removeButton = this.entityItem.filter({hasText: stylesheetName}).getByLabel('Remove'); - await expect(removeButton).toBeVisible(); - await removeButton.click(); + await this.click(removeButton); } // TipTap async deleteToolbarGroup(groupIndex: number, rowIndex: number = 0) { const groupButton = this.tiptapToolbarConfiguration.locator('.row').nth(rowIndex).locator('.group').nth(groupIndex); - await expect(groupButton).toBeVisible(); - await groupButton.hover(); + await this.hover(groupButton); const actionsInGroup = groupButton.locator('.items').locator('uui-button'); const actionsCount = await actionsInGroup.count(); for (let i = 0; i < actionsCount; i++) { - await actionsInGroup.first().click(); + await this.click(actionsInGroup.first()); } - await groupButton.locator('[label="Remove group"]').click(); + await this.click(groupButton.locator('[label="Remove group"]')); } async deleteToolbarRow(rowIndex: number) { const rowButton = this.tiptapToolbarConfiguration.locator('.row').nth(rowIndex); - await expect(rowButton).toBeVisible(); - await rowButton.hover(); - await rowButton.locator('[label="Remove row"]').click(); + await this.hover(rowButton); + await this.click(rowButton.locator('[label="Remove row"]')); } async clickAddRowToolbarButton() { - await expect(this.addRowToolbarBtn).toBeVisible(); - await this.addRowToolbarBtn.click(); + await this.click(this.addRowToolbarBtn); } async clickAddGroupToolbarButton() { - await expect(this.addGroupToolbarBtn).toBeVisible(); - await this.addGroupToolbarBtn.click(); + await this.click(this.addGroupToolbarBtn); } async clickExtensionItemWithName(name: string) { - await expect(this.tiptapExtensionsConfiguration.locator('uui-checkbox[label="' + name + '"]')).toBeVisible(); - await this.tiptapExtensionsConfiguration.locator('uui-checkbox[label="' + name + '"]').click(); + await this.click(this.tiptapExtensionsConfiguration.locator(`uui-checkbox[label="${name}"]`)); } async doesPropertyEditorHaveUiAlias(uiAlias: string) { @@ -1193,13 +1104,11 @@ export class DataTypeUiHelper extends UiBaseLocators { } async clickDataTypeButton() { - await expect(this.dataTypeBtn).toBeVisible(); - await this.dataTypeBtn.click(); + await this.click(this.dataTypeBtn); } async clickDataTypesMenu() { - await expect(this.dataTypesMenu).toBeVisible(); - await this.dataTypesMenu.click(); + await this.click(this.dataTypesMenu); } async doesSettingHaveValue(settings) { @@ -1221,18 +1130,16 @@ export class DataTypeUiHelper extends UiBaseLocators { async clickStatusbarItemInToolboxWithName(name: string) { const statusbarItemLocator = this.tiptapStatusbarConfiguration.locator('#toolbox uui-button').filter({hasText: name}); - await expect(statusbarItemLocator).toBeVisible(); - await statusbarItemLocator.click(); + await this.click(statusbarItemLocator); } async clickStatusbarItemWithName(name: string) { const statusbarItemLocator = this.tiptapStatusbarConfiguration.locator('#statusbar uui-button').filter({hasText: name}); - await expect(statusbarItemLocator).toBeVisible(); - await statusbarItemLocator.click(); + await this.click(statusbarItemLocator); } async isExtensionItemChecked(itemName: string, isChecked: boolean = true) { - await expect(this.tiptapExtensionsConfiguration.locator('uui-checkbox[label="' + itemName + '"] input')).toBeChecked({checked: isChecked}); + await expect(this.tiptapExtensionsConfiguration.locator(`uui-checkbox[label="${itemName}"] input`)).toBeChecked({checked: isChecked}); } async doesBlockHaveThumbnailImage(thumbnailImageUrl: string) { @@ -1241,20 +1148,17 @@ export class DataTypeUiHelper extends UiBaseLocators { async addTimeZones(timeZones: string[]) { for (let i = 0; i < timeZones.length; i++) { - expect(this.timeZoneDropDown).toBeVisible(); - await this.timeZoneDropDown.click(); - await this.timeZoneDropDown.getByText(timeZones[i]).click(); - await this.addTimeZoneBtn.click(); + await this.click(this.timeZoneDropDown); + await this.click(this.timeZoneDropDown.getByText(timeZones[i])); + await this.click(this.addTimeZoneBtn); } } - - async clickChooseDataSourceButton(){ - await expect(this.dataSourceChooseBtn).toBeVisible(); - await this.dataSourceChooseBtn.click(); + + async clickChooseDataSourceButton() { + await this.click(this.dataSourceChooseBtn); } async clickChooseThumbnailButton() { - await expect(this.chooseThumbnailAlias).toBeVisible(); - await this.chooseThumbnailAlias.click(); + await this.click(this.chooseThumbnailAlias); } } \ No newline at end of file diff --git a/lib/helpers/ExamineManagementUiHelper.ts b/lib/helpers/ExamineManagementUiHelper.ts index 16d5b7e3..a2100162 100644 --- a/lib/helpers/ExamineManagementUiHelper.ts +++ b/lib/helpers/ExamineManagementUiHelper.ts @@ -16,7 +16,7 @@ export class ExamineManagementUiHelper extends UiBaseLocators { } async clickExamineManagementTab() { - await this.examineManagementTab.click(); + await this.click(this.examineManagementTab); } async doesIndexersHaveText(text: string) { @@ -28,7 +28,7 @@ export class ExamineManagementUiHelper extends UiBaseLocators { } async clickIndexByName(indexName: string) { - await this.page.getByRole('link', { name: indexName }).click(); + await this.click(this.page.getByRole('link', {name: indexName})); } async doesIndexPropertyHaveValue(indexProperty: string, indexValue: string) { @@ -36,6 +36,6 @@ export class ExamineManagementUiHelper extends UiBaseLocators { } async doesIndexHaveHealthStatus(indexName: string, status: string) { - return expect(this.page.locator("[headline='" + indexName + "']").getByText(status)).toBeVisible(); + return expect(this.page.locator(`[headline='${indexName}']`).getByText(status)).toBeVisible(); } } diff --git a/lib/helpers/FormsUiHelper.ts b/lib/helpers/FormsUiHelper.ts index a7102045..48202335 100644 --- a/lib/helpers/FormsUiHelper.ts +++ b/lib/helpers/FormsUiHelper.ts @@ -1,7 +1,8 @@ import { UiBaseLocators } from "./UiBaseLocators"; -import { expect, Locator, Page } from "@playwright/test" +import { expect, Locator, Page } from "@playwright/test"; +import { ConstantHelper } from "./ConstantHelper"; -export class FormsUiHelper extends UiBaseLocators{ +export class FormsUiHelper extends UiBaseLocators { private readonly quickCreateNewBtn: Locator; private readonly createNewFormModalBtn: Locator; private readonly saveFormBtn: Locator; @@ -136,59 +137,55 @@ export class FormsUiHelper extends UiBaseLocators{ */ async clickQuickCreateFormButton() { - await this.formMenuItemForForm.hover(); - await this.formMenuItemForForm.locator(this.quickCreateNewBtn).click(); + await this.hover(this.formMenuItemForForm); + await this.click(this.formMenuItemForForm.locator(this.quickCreateNewBtn)); } async clickNewFormButton() { - await expect(this.createNewFormModalBtn).toBeVisible(); - await this.createNewFormModalBtn.click(); + await this.click(this.createNewFormModalBtn); } - async clickSaveFormButton(){ - await this.saveFormBtn.click(); + async clickSaveFormButton() { + await this.click(this.saveFormBtn); } - async fillFormName(name: string){ - await expect(this.formNameTxt).toBeVisible(); - await this.formNameTxt.fill(name); + async fillFormName(name: string) { + await this.enterText(this.formNameTxt, name); } - async fillFormPageName(position: number, name: string){ + async fillFormPageName(position: number, name: string) { const nameInput = this.formPageNametxt.nth(position); - await expect(nameInput).toBeVisible(); - await nameInput.fill(name); + await this.enterText(nameInput, name); } - async fillFormGroupName(position: number, name: string){ + async fillFormGroupName(position: number, name: string) { const groupInput = this.formGroupNameTxt.nth(position); - await expect(groupInput).toBeVisible(); - await groupInput.fill(name); + await this.enterText(groupInput, name); } - async fillFormFieldName(name: string){ + async fillFormFieldName(name: string) { await this.formEditFieldModal.locator(this.formFieldNameTxt).fill(name); } - async clickAddNewPageButton(){ - await this.formAddNewPageBtn.click(); + async clickAddNewPageButton() { + await this.click(this.formAddNewPageBtn); } - async clickAddNewGroupButton(){ - await this.formAddNewGroupBtn.click(); + async clickAddNewGroupButton() { + await this.click(this.formAddNewGroupBtn); } - async clickAddQuestionButton(index: number = 0){ + async clickAddQuestionButton(index: number = 0) { const button = this.formPage.nth(index).locator(this.formAddQuestionBtn); - await button.click(); + await this.click(button); } - async chooseFormFieldType(type: string){ - await this.formFieldType.filter({hasText: type}).nth(0).click(); + async chooseFormFieldType(type: string) { + await this.click(this.formFieldType.filter({hasText: type}).nth(0)); } - async clickExpandFormsTreeButton(){ - await this.formExpandBtn.click(); + async clickExpandFormsTreeButton() { + await this.click(this.formExpandBtn); } async doesFormTreeHaveFormName(name: string) { @@ -196,300 +193,274 @@ export class FormsUiHelper extends UiBaseLocators{ } async goToFormWithName(name: string) { - await this.formTree.getByText(name, {exact: true}).click(); + await this.click(this.formTree.getByText(name, {exact: true})); } async clickFormFieldTypeSubmitModal() { - await this.formSubmitButtonModal.click(); + await this.click(this.formSubmitButtonModal); } - async clickActionMenuOnFormMenuItem(name: string){ - await this.menuItem.locator('[label="' + name + '"] uui-button[label="Open actions menu"]').click(); + async clickActionMenuOnFormMenuItem(name: string) { + await this.click(this.menuItem.locator(`[label="${name}"] uui-button[label="Open actions menu"]`)); } - async clickDeleteFormButton(){ - await this.formActionModal.locator(this.formDeleteThreeDotBtn).click(); - await this.deleteExactBtn.click(); + async clickDeleteFormButton() { + await this.click(this.formActionModal.locator(this.formDeleteThreeDotBtn)); + await this.click(this.deleteExactBtn); } - async goToFormSetting(){ - await this.formWorkspaceEditor.locator(this.formSettingIcon).click(); + async goToFormSetting() { + await this.click(this.formWorkspaceEditor.locator(this.formSettingIcon)); } - async setFormStoreRecordsSetting(){ - await expect(this.formSettingStoreRecordBtn).toBeVisible(); + async setFormStoreRecordsSetting() { + await this.waitForVisible(this.formSettingStoreRecordBtn); const toggle = this.formSettingStoreRecordBtn.locator(this.formToggleSlider); - await expect(toggle).toBeVisible(); + await this.waitForVisible(toggle); await toggle.check(); } - async setFormCaptionsSetting(){ - await expect(this.formSettingCaptionsContainer).toBeVisible(); + async setFormCaptionsSetting() { + await this.waitForVisible(this.formSettingCaptionsContainer); for (let i = 0; i < 3; i++) { const captionInput = this.formSettingCaptions.locator(this.formInputTxt).nth(i); - await expect(captionInput).toBeVisible(); - await captionInput.fill("Test Caption " + (i + 1)); + await this.enterText(captionInput, `Test Caption ${i + 1}`); } } - async setFormStylingSetting(){ - await expect(this.formSettingStylingContainer).toBeVisible(); + async setFormStylingSetting() { + await this.waitForVisible(this.formSettingStylingContainer); const cssClassInput = this.formSettingStyling.locator(this.formInputTxt); - await expect(cssClassInput).toBeVisible(); - await cssClassInput.fill("custom-css-class"); + await this.enterText(cssClassInput, "custom-css-class"); const disableDefaultStylesheetInput = this.formSettingStyling.locator(this.formToggleSlider); - await expect(disableDefaultStylesheetInput).toBeVisible(); - await disableDefaultStylesheetInput.click(); + await this.click(disableDefaultStylesheetInput); } - async setFormValidationSetting(){ - await expect(this.formSettingValidationContainer).toBeVisible(); + async setFormValidationSetting() { + await this.waitForVisible(this.formSettingValidationContainer); const requiredErrorMessageInput = this.formSettingValidation.locator(this.formInputTxt).nth(0); - await expect(requiredErrorMessageInput).toBeVisible(); - await requiredErrorMessageInput.fill("Required error message"); + await this.enterText(requiredErrorMessageInput, "Required error message"); const invalidErrorMessageInput = this.formSettingValidation.locator(this.formInputTxt).nth(1); - await expect(invalidErrorMessageInput).toBeVisible(); - await invalidErrorMessageInput.fill("Invalid error message"); + await this.enterText(invalidErrorMessageInput, "Invalid error message"); const showValidationSummaryInput = this.formSettingValidation.locator(this.formToggleSlider).nth(0); - await expect(showValidationSummaryInput).toBeVisible(); - await showValidationSummaryInput.click(); + await this.click(showValidationSummaryInput); const hideFieldValidationInput = this.formSettingValidation.locator(this.formToggleSlider).nth(1); - await expect(hideFieldValidationInput).toBeVisible(); - await hideFieldValidationInput.click(); + await this.click(hideFieldValidationInput); const markMandatoryFieldRadioInput = this.formSettingValidation.locator("uui-radio[value = 'MarkMandatoryFields']"); - await expect(markMandatoryFieldRadioInput).toBeVisible(); - await markMandatoryFieldRadioInput.click(); + await this.click(markMandatoryFieldRadioInput); const indicatorInput = this.formSettingValidation.locator(this.formInputTxt).nth(2); - await expect(indicatorInput).toBeVisible(); - await indicatorInput.fill("+"); + await this.enterText(indicatorInput, "+"); } - async setFormAutocompleteSetting(){ - await expect(this.formSettingAutocompleteContainer).toBeVisible(); + async setFormAutocompleteSetting() { + await this.waitForVisible(this.formSettingAutocompleteContainer); const autocompleteAttributeRadioInput = this.formSettingAutocomplete.locator('uui-radio[value = "On"]'); - await expect(autocompleteAttributeRadioInput).toBeVisible(); - await autocompleteAttributeRadioInput.click(); + await this.click(autocompleteAttributeRadioInput); } - async setFormModerationSetting(){ - await expect(this.formSettingModerationContainer).toBeVisible(); + async setFormModerationSetting() { + await this.waitForVisible(this.formSettingModerationContainer); const enablePostModerationAttributeToggleInput = this.formSettingModeration.locator(this.formToggleSlider); - await expect(enablePostModerationAttributeToggleInput).toBeVisible(); - await enablePostModerationAttributeToggleInput.click(); + await this.click(enablePostModerationAttributeToggleInput); } - async setFormFieldsDisplayedSetting(){ - await expect(this.formSettingFieldsDisplayedContainer).toBeVisible(); + async setFormFieldsDisplayedSetting() { + await this.waitForVisible(this.formSettingFieldsDisplayedContainer); const displayDefaultFieldsToggleInput = this.formSettingFieldsDisplayed.locator(this.formToggleSlider); - await expect(displayDefaultFieldsToggleInput).toBeVisible(); - await displayDefaultFieldsToggleInput.click(); - await this.page.waitForTimeout(100); // short pause required here otherwise revealed elements are not found + await this.click(displayDefaultFieldsToggleInput); + await this.page.waitForTimeout(ConstantHelper.wait.minimal); // short pause required here otherwise revealed elements are not found const displayFieldsSelect = this.formSettingFieldsDisplayed.locator("select"); - await expect(displayFieldsSelect).toBeVisible(); + await this.waitForVisible(displayFieldsSelect); await displayFieldsSelect.selectOption({ value: '_system_state' }); const displayFieldsAddButton = this.formSettingFieldsDisplayed.locator("button[id='button']"); - await expect(displayFieldsAddButton).toBeVisible(); - await displayFieldsAddButton.click(); + await this.click(displayFieldsAddButton); } - async setFormDataRetentionSetting(recordNumber: string){ - await expect(this.formSettingDataRetentionContainer).toBeVisible(); + async setFormDataRetentionSetting(recordNumber: string) { + await this.waitForVisible(this.formSettingDataRetentionContainer); const retainSubmittedRecordsToggleInput = this.formSettingDataRetention.locator(this.formToggleSlider).nth(0); - await expect(retainSubmittedRecordsToggleInput).toBeVisible(); - await retainSubmittedRecordsToggleInput.click(); - await this.page.waitForTimeout(100); // short pause required here otherwise revealed elements are not found + await this.click(retainSubmittedRecordsToggleInput); + await this.page.waitForTimeout(ConstantHelper.wait.minimal); // short pause required here otherwise revealed elements are not found const retainSubmittedRecordsNumberInput = this.formSettingDataRetention.locator(this.formInputNumber).nth(0); - await expect(retainSubmittedRecordsNumberInput).toBeVisible(); - await retainSubmittedRecordsNumberInput.fill(recordNumber); + await this.enterText(retainSubmittedRecordsNumberInput, recordNumber); } async toggleFieldSetting(settingAlias: string) { - const settingFieldLocator = this.page.locator('umb-property-layout[alias="' + settingAlias + '"] #toggle'); - await expect(settingFieldLocator).toBeVisible(); - await settingFieldLocator.click(); + const settingFieldLocator = this.page.locator(`umb-property-layout[alias="${settingAlias}"] #toggle`); + await this.click(settingFieldLocator); } async applyFieldSettingViaTextInput(settingAlias: string, settingValue: string) { - const settingFieldLocator = this.page.locator('umb-property[alias="' + settingAlias + '"] input'); + const settingFieldLocator = this.page.locator(`umb-property[alias="${settingAlias}"] input`); await settingFieldLocator.fill(settingValue); } async applyFieldSettingViaDropDown(settingAlias: string, settingValue: string) { - const settingFieldLocator = this.page.locator('umb-property[alias="' + settingAlias + '"] select'); + const settingFieldLocator = this.page.locator(`umb-property[alias="${settingAlias}"] select`); await settingFieldLocator.selectOption({ value: settingValue }); } async applyFieldSettingViaSlider(settingAlias: string) { - const settingFieldLocator = this.page.locator('umb-property[alias="' + settingAlias + '"] #toggle'); - await expect(settingFieldLocator).toBeVisible(); - await settingFieldLocator.click(); + const settingFieldLocator = this.page.locator(`umb-property[alias="${settingAlias}"] #toggle`); + await this.click(settingFieldLocator); } async applyFieldFileUploadSettings(settingAlias: string, allowedProvidedExtensions: Array, allowedCustomExtensions: Array, allowMultiple: boolean) { - const settingFieldLocator = this.page.locator('umb-property-layout[alias="' + settingAlias + '"]'); + const settingFieldLocator = this.page.locator(`umb-property-layout[alias="${settingAlias}"]`); for (var i = 0; i < allowedProvidedExtensions.length; i++) { const checkBoxLocator = settingFieldLocator.locator('uui-toggle', {hasText: allowedProvidedExtensions[i].toUpperCase()}).locator('#toggle'); - await expect(checkBoxLocator).toBeVisible(); - await checkBoxLocator.click(); + await this.click(checkBoxLocator); } - + const addNewExtensionLocator = settingFieldLocator.locator('input[placeholder = "Add new allowed file type"]'); - await expect(addNewExtensionLocator).toBeVisible(); + await this.waitForVisible(addNewExtensionLocator); const buttonLocator = settingFieldLocator.locator('form svg'); - await expect(buttonLocator).toBeVisible(); + await this.waitForVisible(buttonLocator); for (var i = 0; i < allowedCustomExtensions.length; i++) { await addNewExtensionLocator.fill(allowedCustomExtensions[i]); - await buttonLocator.click(); + await this.click(buttonLocator); } - + if (allowMultiple) { const alias = "allowMultipleFileUploads"; - const multipleUploadLocator = this.page.locator('umb-property-layout[alias="' + alias + '"] #toggle'); - await expect(multipleUploadLocator).toBeVisible(); - await multipleUploadLocator.click(); + const multipleUploadLocator = this.page.locator(`umb-property-layout[alias="${alias}"] #toggle`); + await this.click(multipleUploadLocator); } } async applyFieldPrevalues(settingAlias: string, prevalues: Array) { - const settingFieldLocator = this.page.locator('umb-property-layout[alias="' + settingAlias + '"]'); + const settingFieldLocator = this.page.locator(`umb-property-layout[alias="${settingAlias}"]`); for (var i = 0; i < prevalues.length; i++) { const valueFieldLocator = settingFieldLocator.locator("input[placeholder = 'New value']"); - await expect(valueFieldLocator).toBeVisible(); + await this.waitForVisible(valueFieldLocator); await valueFieldLocator.fill(prevalues[i].value); - + const captionFieldLocator = settingFieldLocator.locator("input[placeholder = 'New caption']"); - await expect(captionFieldLocator).toBeVisible(); + await this.waitForVisible(captionFieldLocator); await captionFieldLocator.fill(prevalues[i].caption); - + const buttonLocator = settingFieldLocator.locator('uui-button[label="add"]'); - await expect(buttonLocator).toBeVisible(); - await buttonLocator.click(); + await this.click(buttonLocator); } } async applyFieldSettingViaTextArea(settingAlias: string, settingValue: string) { - const settingFieldLocator = this.page.locator('umb-property[alias="' + settingAlias + '"] textarea'); + const settingFieldLocator = this.page.locator(`umb-property[alias="${settingAlias}"] textarea`); await settingFieldLocator.fill(settingValue); } async applyFieldSettingViaRichTextInput(settingAlias: string, settingValue: string) { - const richTextAreaTxt = this.page.locator('umb-property[alias="' + settingAlias + '"] umb-property-editor-ui-tiptap').locator('#editor .tiptap'); - await expect(richTextAreaTxt).toBeVisible(); + const richTextAreaTxt = this.page.locator(`umb-property[alias="${settingAlias}"] umb-property-editor-ui-tiptap`).locator('#editor .tiptap'); + await this.waitForVisible(richTextAreaTxt); await richTextAreaTxt.fill(settingValue); } async applyFieldSettingViaRange(settingAlias: string, settingValue: string) { - const settingFieldLocator = this.page.locator('umb-property[alias="' + settingAlias + '"]'); + const settingFieldLocator = this.page.locator(`umb-property[alias="${settingAlias}"]`); await settingFieldLocator.locator('input[type="range"]').fill(settingValue); } async applyFieldSettingViaFieldMappingInput(settingAlias: string, settingValue: Array) { - const settingFieldLocator = this.page.locator('umb-property[alias="' + settingAlias + '"]'); - await expect(settingFieldLocator).toBeVisible(); - + const settingFieldLocator = this.page.locator(`umb-property[alias="${settingAlias}"]`); + await this.waitForVisible(settingFieldLocator); + for (let i = 0; i < settingValue.length; i++) { const buttonLocator = settingFieldLocator.locator('uui-button[label="add"]'); - await expect(buttonLocator).toBeVisible(); - await buttonLocator.click(); - + await this.click(buttonLocator); + const aliasInputLocator = settingFieldLocator.locator("input[placeholder = 'Alias']").nth(i); - await expect(aliasInputLocator).toBeVisible(); + await this.waitForVisible(aliasInputLocator); await aliasInputLocator.fill(settingValue[i].alias); - + const staticValueInputLocator = settingFieldLocator.locator("input[placeholder = 'Static value']").nth(i); - await expect(staticValueInputLocator).toBeVisible(); + await this.waitForVisible(staticValueInputLocator); await staticValueInputLocator.fill(settingValue[i].staticValue); } } async applyFieldSettingViaDocumentMapper(settingAlias: string, settingValue: any) { - const settingFieldLocator = this.page.locator('umb-property[alias="' + settingAlias + '"]'); - await expect(settingFieldLocator).toBeVisible(); - + const settingFieldLocator = this.page.locator(`umb-property[alias="${settingAlias}"]`); + await this.waitForVisible(settingFieldLocator); + const selectLocator = settingFieldLocator.locator("forms-document-mapper-property-editor select"); await selectLocator.selectOption({ value : settingValue.doctype }); - + const inputLocator = settingFieldLocator.locator('forms-document-mapper-property-editor input[type = "text"]'); - await expect(inputLocator.first()).toBeVisible(); + await this.waitForVisible(inputLocator.first()); const inputLocatorCount = await inputLocator.count(); for (let i = 0; i < inputLocatorCount; i++) { - await expect(inputLocator.nth(i)).toBeVisible(); + await this.waitForVisible(inputLocator.nth(i)); await inputLocator.nth(i).fill(settingValue.nameStaticValue); } } async applyFieldSettingViaEmailTemplatePicker(settingAlias: string, settingValue: string) { - const settingFieldLocator = this.page.locator('umb-property[alias="' + settingAlias + '"]'); - await expect(settingFieldLocator).toBeVisible(); - + const settingFieldLocator = this.page.locator(`umb-property[alias="${settingAlias}"]`); + await this.waitForVisible(settingFieldLocator); + const buttonLocator = settingFieldLocator.locator("#caret-button"); - await buttonLocator.click(); - + await this.click(buttonLocator); + const templateLocator = this.page.locator("#label-button", {hasText: settingValue}); - await expect(templateLocator).toBeVisible(); - await templateLocator.click(); + await this.click(templateLocator); } async applyFieldSettingViaStandardFieldMappingInput(settingAlias: string, settingValue: Array) { - const settingFieldLocator = this.page.locator('umb-property[alias="' + settingAlias + '"]'); - await expect(settingFieldLocator).toBeVisible(); - + const settingFieldLocator = this.page.locator(`umb-property[alias="${settingAlias}"]`); + await this.waitForVisible(settingFieldLocator); + for (let i = 0; i < settingValue.length; i++) { if (settingValue[i].include) { - const includeButtonLocator = settingFieldLocator.locator('div[data-umb-standard-field-mapping-include="' + settingValue[i].alias + '"] #toggle'); - await includeButtonLocator.click(); + const includeButtonLocator = settingFieldLocator.locator(`div[data-umb-standard-field-mapping-include="${settingValue[i].alias}"] #toggle`); + await this.click(includeButtonLocator); } - - const keyNameButtonLocator = settingFieldLocator.locator('div[data-umb-standard-field-mapping-key-name="' + settingValue[i].alias + '"] input[type="text"]'); + + const keyNameButtonLocator = settingFieldLocator.locator(`div[data-umb-standard-field-mapping-key-name="${settingValue[i].alias}"] input[type="text"]`); await keyNameButtonLocator.fill(settingValue[0].keyName); } } async setFieldMandatory(message: string) { - await expect(this.formFieldMandatory).toBeVisible(); - await this.formFieldMandatory.locator("#toggle").click(); - await this.page.waitForTimeout(1000); + await this.click(this.formFieldMandatory.locator("#toggle")); + await this.page.waitForTimeout(ConstantHelper.wait.medium); const inputLocator = this.formFieldMandatory.locator(this.formInputTxt); - await expect(inputLocator).toBeVisible(); - await inputLocator.fill(message); + await this.enterText(inputLocator, message); } - + async setFieldValidation(label: string, message: string) { - await expect(this.formFieldRegex).toBeVisible(); + await this.waitForVisible(this.formFieldRegex); const selectLocator = this.formFieldRegex.locator("select"); await selectLocator.selectOption({ label: label }); - await this.page.waitForTimeout(1000); + await this.page.waitForTimeout(ConstantHelper.wait.medium); const inputLocator = this.formFieldRegex.locator("input"); - await expect(inputLocator).toBeVisible(); - await inputLocator.fill(message); + await this.enterText(inputLocator, message); } - async clickFormWorkflowConfigureButton(){ - await this.formWorkflowConfigureBtn.click(); + async clickFormWorkflowConfigureButton() { + await this.click(this.formWorkflowConfigureBtn); } async clickFormWorkflowEditSubmitButton() { - await this.formEditWorkflowModal.locator(this.formSubmitButtonModal).click(); + await this.click(this.formEditWorkflowModal.locator(this.formSubmitButtonModal)); } async clickFormWorkflowConfigureSubmitButton() { - await this.formConfigureWorkflowModal.locator(this.formSubmitButtonModal).click(); + await this.click(this.formConfigureWorkflowModal.locator(this.formSubmitButtonModal)); } - async clickFormWorkflowAddButton(){ - await this.formWorkflowOnSubmitStage.locator(this.formWorkflowAddButtonModal).click({force: true}); + async clickFormWorkflowAddButton() { + await this.click(this.formWorkflowOnSubmitStage.locator(this.formWorkflowAddButtonModal), {force: true}); } - async selectWorkflowType(workflowType: string){ - this.page.locator('umb-ref-item[title="' + workflowType + '"]').click(); + async selectWorkflowType(workflowType: string) { + await this.click(this.page.locator(`umb-ref-item[title="${workflowType}"]`)); } async fillWorkflowName(workflowName: string) { - await expect(this.formWorkflowNameTxt).toBeVisible(); - await this.formWorkflowNameTxt.fill(workflowName); + await this.enterText(this.formWorkflowNameTxt, workflowName); } /* @@ -497,61 +468,56 @@ export class FormsUiHelper extends UiBaseLocators{ */ async clickQuickCreatePrevalueSourceButton() { - await expect(this.formMenuItemForPrevalueSource).toBeVisible(); - await this.formMenuItemForPrevalueSource.hover(); - await this.formMenuItemForPrevalueSource.locator(this.quickCreateNewBtn).click(); + await this.hover(this.formMenuItemForPrevalueSource); + await this.click(this.formMenuItemForPrevalueSource.locator(this.quickCreateNewBtn)); } async clickPrevalueSourceTypeButton(type: string) { const button = this.createNewPrevaluesourceModalBtn.locator("#name", {hasText: type}); - await expect(button).toBeVisible(); - await button.click(); + await this.click(button); } - async clickExpandPrevalueSourceTreeButton(){ - await this.prevalueSourceExpandBtn.click(); + async clickExpandPrevalueSourceTreeButton() { + await this.click(this.prevalueSourceExpandBtn); } async goToPrevalueSourceWithName(name: string) { - await this.prevalueSourceTree.locator('uui-menu-item[label="' + name + '"]').click(); + await this.click(this.prevalueSourceTree.locator(`uui-menu-item[label="${name}"]`)); } - async clickDeletePrevalueSourceButton(name: string){ - const prevalueSource = await this.prevalueSourceTree.locator('uui-menu-item[label="' + name + '"]'); - await prevalueSource.locator(this.prevalueSourceDeleteBtn).click(); - await this.deleteExactBtn.click(); + async clickDeletePrevalueSourceButton(name: string) { + const prevalueSource = this.prevalueSourceTree.locator(`uui-menu-item[label="${name}"]`); + await this.click(prevalueSource.locator(this.prevalueSourceDeleteBtn)); + await this.click(this.deleteExactBtn); } async applyCacheOptions(option: string, timeValue: number = 0, timeUnit: string = "") { - await expect(this.prevalueSourceCacheContainer).toBeVisible(); - const optionSelect = this.prevalueSourceCacheContainer.locator('uui-radio[value = "' + option + '"]'); - await expect(optionSelect).toBeVisible(); - await optionSelect.click(); - + await this.waitForVisible(this.prevalueSourceCacheContainer); + const optionSelect = this.prevalueSourceCacheContainer.locator(`uui-radio[value = "${option}"]`); + await this.click(optionSelect); + if (option === "time") { const numberInput = this.prevalueSourceCacheContainer.locator("input[type='number']"); - await expect(numberInput).toBeVisible(); + await this.waitForVisible(numberInput); await numberInput.fill(timeValue.toString()); - + const unitSelect = this.prevalueSourceCacheContainer.locator("select"); - await expect(unitSelect).toBeVisible(); + await this.waitForVisible(unitSelect); await unitSelect.selectOption({ value: timeUnit }); } } async applyPrevalueSourceSettingViaNodeSelector(labelText: string, settingValue: string) { - const container = this.page.locator('umb-property[alias="' + labelText + '"]'); - await expect(container).toBeVisible(); + const container = this.page.locator(`umb-property[alias="${labelText}"]`); + await this.waitForVisible(container); const rootNode = container.locator('uui-button[label="Specify root node"]'); - await expect(rootNode).toBeVisible(); - await rootNode.click(); - await expect(this.prevalueSourceOriginModal).toBeVisible(); - const value = this.prevalueSourceOriginModal.locator('umb-ref-item[name="' + settingValue + '"]'); - await expect(value).toBeVisible(); - await value.click(); + await this.click(rootNode); + await this.waitForVisible(this.prevalueSourceOriginModal); + const value = this.prevalueSourceOriginModal.locator(`umb-ref-item[name="${settingValue}"]`); + await this.click(value); } - async checkPrevalueSourceTypeLabel(){ - await expect(this.prevalueSourceTypeLabel).toBeVisible(); + async checkPrevalueSourceTypeLabel() { + await this.waitForVisible(this.prevalueSourceTypeLabel); } } \ No newline at end of file diff --git a/lib/helpers/HealthCheckUiHelper.ts b/lib/helpers/HealthCheckUiHelper.ts index ca65335f..29495f39 100644 --- a/lib/helpers/HealthCheckUiHelper.ts +++ b/lib/helpers/HealthCheckUiHelper.ts @@ -24,7 +24,7 @@ export class HealthCheckUiHelper extends UiBaseLocators { } async clickHealthCheckTab() { - await this.healthCheckTab.click(); + await this.click(this.healthCheckTab); } async checkHealthCheckGroupCount() { @@ -33,11 +33,11 @@ export class HealthCheckUiHelper extends UiBaseLocators { } async clickPerformanceAllChecksButton() { - await this.performanceAllChecksBtn.click(); + await this.click(this.performanceAllChecksBtn); } async clickHeathCheckGroupByName(groupName: string) { - await this.page.getByRole('link', { name: groupName }).click(); + await this.click(this.page.getByRole('link', {name: groupName})); } async isHealthCheckGroupVisible(groupName: string) { diff --git a/lib/helpers/LogViewerUiHelper.ts b/lib/helpers/LogViewerUiHelper.ts index f51b9213..5162d3b8 100644 --- a/lib/helpers/LogViewerUiHelper.ts +++ b/lib/helpers/LogViewerUiHelper.ts @@ -34,28 +34,24 @@ export class LogViewerUiHelper extends UiBaseLocators { } async clickSearchButton() { - await expect(this.searchBtn).toBeVisible(); - await this.searchBtn.click(); - await expect(this.searchLogsTxt).toBeVisible(); + await this.click(this.searchBtn); + await this.waitForVisible(this.searchLogsTxt); } async clickOverviewButton() { - await expect(this.overviewBtn).toBeVisible(); - await this.overviewBtn.click(); + await this.click(this.overviewBtn); } async enterSearchKeyword(keyword: string) { - await this.searchLogsTxt.clear(); - await this.searchLogsTxt.fill(keyword); + await this.enterText(this.searchLogsTxt, keyword); } async selectLogLevel(level: string) { - await expect(this.selectLogLevelBtn).toBeVisible(); // The force click is necessary. - await this.selectLogLevelBtn.click({force: true}); + await this.click(this.selectLogLevelBtn, {force: true}); const logLevelLocator = this.page.locator('.log-level-menu-item').getByText(level); - await expect(logLevelLocator).toBeVisible(); - await logLevelLocator.click({force: true}); + // Force click is needed + await this.click(logLevelLocator, {force: true}); } async doesLogLevelIndicatorDisplay(level: string) { @@ -67,12 +63,10 @@ export class LogViewerUiHelper extends UiBaseLocators { } async saveSearch(searchName: string) { - await expect(this.saveSearchHeartIcon).toBeVisible(); // The force click is necessary. - await this.saveSearchHeartIcon.click({force: true}); - await this.searchNameTxt.clear(); - await this.searchNameTxt.fill(searchName); - await this.saveSearchBtn.click(); + await this.click(this.saveSearchHeartIcon, {force: true}); + await this.enterText(this.searchNameTxt, searchName); + await this.click(this.saveSearchBtn); } checkSavedSearch(searchName: string) { @@ -80,7 +74,7 @@ export class LogViewerUiHelper extends UiBaseLocators { } async clickSortLogByTimestampButton() { - await this.sortLogByTimestampBtn.click(); + await this.click(this.sortLogByTimestampBtn); } async doesFirstLogHaveTimestamp(timestamp: string) { @@ -88,7 +82,7 @@ export class LogViewerUiHelper extends UiBaseLocators { } async clickPageNumber(pageNumber: number) { - await this.page.getByLabel('Go to page ' + pageNumber, {exact: true}).click(); + await this.click(this.page.getByLabel(`Go to page ${pageNumber}`, {exact: true})); } async doesFirstLogHaveMessage(message: string) { @@ -96,8 +90,7 @@ export class LogViewerUiHelper extends UiBaseLocators { } async clickSavedSearchByName(name: string) { - await expect(this.page.locator('#saved-searches').getByLabel(name)).toBeVisible(); - await this.page.locator('#saved-searches').getByLabel(name).click(); + await this.click(this.page.locator('#saved-searches').getByLabel(name)); } async doesSearchBoxHaveValue(searchValue: string) { @@ -105,7 +98,7 @@ export class LogViewerUiHelper extends UiBaseLocators { } async clickFirstLogSearchResult() { - await this.firstLogSearchResult.click(); + await this.click(this.firstLogSearchResult); } async doesDetailedLogHaveText(text: string) { @@ -113,16 +106,14 @@ export class LogViewerUiHelper extends UiBaseLocators { } async clickSavedSearchesButton() { - await expect(this.savedSearchesBtn).toBeVisible(); // The force click is necessary. - await this.savedSearchesBtn.click({force: true}); + await this.click(this.savedSearchesBtn, {force: true}); } async removeSavedSearchByName(name: string) { const removedSavedSearchWithNameLocator = this.page.locator('li').filter({hasText: name}).getByLabel('Remove saved search'); - await expect(removedSavedSearchWithNameLocator).toBeVisible(); // The force click is necessary. - await removedSavedSearchWithNameLocator.click({force: true}); + await this.click(removedSavedSearchWithNameLocator, {force: true}); } async waitUntilLoadingSpinnerInvisible() { diff --git a/lib/helpers/MediaTypeUiHelper.ts b/lib/helpers/MediaTypeUiHelper.ts index 05f48971..8942c15c 100644 --- a/lib/helpers/MediaTypeUiHelper.ts +++ b/lib/helpers/MediaTypeUiHelper.ts @@ -1,5 +1,5 @@ import {UiBaseLocators} from "./UiBaseLocators"; -import {expect, Locator, Page} from "@playwright/test"; +import {Locator, Page} from "@playwright/test"; export class MediaTypeUiHelper extends UiBaseLocators { private readonly newMediaTypeThreeDotsBtn: Locator; diff --git a/lib/helpers/ModelsBuilderUiHelper.ts b/lib/helpers/ModelsBuilderUiHelper.ts index 8a4919dd..70cad57d 100644 --- a/lib/helpers/ModelsBuilderUiHelper.ts +++ b/lib/helpers/ModelsBuilderUiHelper.ts @@ -12,7 +12,7 @@ export class ModelsBuilderUiHelper extends UiBaseLocators { } async clickModelsBuilderTab() { - await this.modelsBuilderTab.click(); + await this.click(this.modelsBuilderTab); } async doesModelsBuilderDashboardHaveText(text: string) { diff --git a/lib/helpers/ProfilingUiHelper.ts b/lib/helpers/ProfilingUiHelper.ts index fb1dd617..eb8bb39e 100644 --- a/lib/helpers/ProfilingUiHelper.ts +++ b/lib/helpers/ProfilingUiHelper.ts @@ -14,13 +14,11 @@ export class ProfilingUiHelper extends UiBaseLocators { } async clickProfilingTab() { - await expect(this.profilingTab).toBeVisible(); - await this.profilingTab.click(); + await this.click(this.profilingTab); } async clickActivateProfilerByDefaultToggle() { - await expect(this.activateProfilerByDefaultToggle).toBeVisible(); - await this.activateProfilerByDefaultToggle.click(); + await this.click(this.activateProfilerByDefaultToggle); } async isActivateProfilerByDefaultToggleChecked(isChecked: boolean) { diff --git a/lib/helpers/PublishedStatusUiHelper.ts b/lib/helpers/PublishedStatusUiHelper.ts index 80a68f71..c493f639 100644 --- a/lib/helpers/PublishedStatusUiHelper.ts +++ b/lib/helpers/PublishedStatusUiHelper.ts @@ -22,27 +22,27 @@ export class PublishedStatusUiHelper extends UiBaseLocators { } async clickPublishedStatusTab() { - await this.publishedStatusTab.click(); + await this.click(this.publishedStatusTab); } async clickRefreshStatusButton() { - await this.refreshStatusBtn.click(); + await this.click(this.refreshStatusBtn); } async clickReloadMemoryCacheButton() { - await this.reloadMemoryCacheBtn.click(); + await this.click(this.reloadMemoryCacheBtn); } async clickRebuildDatabaseCacheButton() { - await this.rebuildDatabaseCacheBtn.click(); + await this.click(this.rebuildDatabaseCacheBtn); } async clickSnapshotInternalCacheButton() { - await this.snapshotInternalCacheBtn.click(); + await this.click(this.snapshotInternalCacheBtn); } async clickContinueButton() { - await this.continueBtn.click(); + await this.click(this.continueBtn); } async isPublishedCacheStatusVisible(status: string) { diff --git a/lib/helpers/RedirectManagementUiHelper.ts b/lib/helpers/RedirectManagementUiHelper.ts index 42ab226f..bbc27809 100644 --- a/lib/helpers/RedirectManagementUiHelper.ts +++ b/lib/helpers/RedirectManagementUiHelper.ts @@ -22,31 +22,27 @@ export class RedirectManagementUiHelper extends UiBaseLocators { } async clickRedirectManagementTab() { - await expect(this.redirectManagementTab).toBeVisible(); - await this.redirectManagementTab.click(); + await this.click(this.redirectManagementTab); } async clickEnableURLTrackerButton() { - await this.enableURLTrackerBtn.click(); + await this.click(this.enableURLTrackerBtn); } async clickDisableURLTrackerButton() { - await this.disableURLTrackerBtn.click(); + await this.click(this.disableURLTrackerBtn); } async enterOriginalUrl(url: string) { - await this.originalUrlTxt.clear(); - await this.originalUrlTxt.fill(url); + await this.enterText(this.originalUrlTxt, url); } async clickSearchButton() { - await expect(this.searchBtn).toBeVisible(); - await this.searchBtn.click(); + await this.click(this.searchBtn); } async deleteFirstRedirectURL() { - await expect(this.firstDeleteButton).toBeVisible(); - await this.firstDeleteButton.click(); + await this.click(this.firstDeleteButton); await this.clickConfirmToDeleteButton(); } diff --git a/lib/helpers/TelemetryDataUiHelper.ts b/lib/helpers/TelemetryDataUiHelper.ts index 1a47b178..127bcf62 100644 --- a/lib/helpers/TelemetryDataUiHelper.ts +++ b/lib/helpers/TelemetryDataUiHelper.ts @@ -12,7 +12,7 @@ export class TelemetryDataUiHelper extends UiBaseLocators { } async clickTelemetryDataTab() { - await this.telemetryDataTab.click(); + await this.click(this.telemetryDataTab); } async changeTelemetryDataLevelValue(value: string) { diff --git a/lib/helpers/WebhookUiHelper.ts b/lib/helpers/WebhookUiHelper.ts index b25e94ce..0752102b 100644 --- a/lib/helpers/WebhookUiHelper.ts +++ b/lib/helpers/WebhookUiHelper.ts @@ -1,4 +1,4 @@ -import {Page, Locator, expect} from "@playwright/test" +import {Page, Locator} from "@playwright/test" import {UiBaseLocators} from "./UiBaseLocators"; import {ConstantHelper} from "./ConstantHelper"; diff --git a/lib/helpers/WelcomeDashboardUiHelper.ts b/lib/helpers/WelcomeDashboardUiHelper.ts index 79238a66..1f4e2caa 100644 --- a/lib/helpers/WelcomeDashboardUiHelper.ts +++ b/lib/helpers/WelcomeDashboardUiHelper.ts @@ -13,7 +13,7 @@ export class WelcomeDashboardUiHelper extends UiBaseLocators { } async clickWelcomeTab() { - await this.welcomeTab.click(); + await this.click(this.welcomeTab); } async doesButtonWithLabelInBoxHaveLink(label: string, boxName: string, link: string) { diff --git a/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts b/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts index 22fd9923..5a9093a2 100644 --- a/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts +++ b/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts @@ -1,4 +1,4 @@ -import {Page, Locator, expect} from "@playwright/test"; +import {Page, Locator, expect} from "@playwright/test"; import {UiBaseLocators} from "../UiBaseLocators"; export class InstallUiHelper extends UiBaseLocators { @@ -24,18 +24,15 @@ export class InstallUiHelper extends UiBaseLocators { } async enterName(name: string) { - await expect(this.nameTxt).toBeVisible(); - await this.nameTxt.fill(name); + await this.enterText(this.nameTxt, name); } async enterEmail(email: string) { - await expect(this.emailTxt).toBeVisible(); - await this.emailTxt.fill(email); + await this.enterText(this.emailTxt, email); } async enterPassword(password: string) { - await expect(this.passwordTxt).toBeVisible(); - await this.passwordTxt.fill(password); + await this.enterText(this.passwordTxt, password); } async setDatabaseType(databaseType: string) { @@ -47,7 +44,6 @@ export class InstallUiHelper extends UiBaseLocators { } async clickInstallButton() { - await expect(this.installBtn).toBeVisible(); - await this.installBtn.click(); + await this.click(this.installBtn); } -} \ No newline at end of file +} From 36b83b137f72f5cfd66b498a06b385a8cbd222ea Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 16:26:55 +0700 Subject: [PATCH 10/34] Apply additional enterText refactoring to ContentUiHelper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Further simplify text input methods by using enterText wrapper for remaining clear/fill patterns. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- lib/helpers/ContentUiHelper.ts | 39 +++++++++++----------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index 14c26e78..d9281341 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -723,8 +723,7 @@ export class ContentUiHelper extends UiBaseLocators { // Numeric async enterNumeric(number: number) { - await this.numericTxt.clear(); - await this.numericTxt.fill(number.toString()); + await this.enterText(this.numericTxt, number.toString()); } // Radiobox @@ -811,8 +810,7 @@ export class ContentUiHelper extends UiBaseLocators { } async enterMultipleTextStringValue(value: string) { - await this.multipleTextStringValueTxt.clear(); - await this.multipleTextStringValueTxt.fill(value); + await this.enterText(this.multipleTextStringValueTxt, value); } async addMultipleTextStringItem(value: string) { @@ -860,8 +858,7 @@ export class ContentUiHelper extends UiBaseLocators { } async searchByKeywordInCollection(keyword: string) { - await this.searchTxt.clear(); - await this.searchTxt.fill(keyword); + await this.enterText(this.searchTxt, keyword); await this.searchTxt.press('Enter'); await this.page.waitForTimeout(ConstantHelper.wait.short); } @@ -886,9 +883,7 @@ export class ContentUiHelper extends UiBaseLocators { } async enterNameInContainer(name: string) { - await expect(this.enterNameInContainerTxt).toBeVisible(); - await this.enterNameInContainerTxt.clear(); - await this.enterNameInContainerTxt.fill(name); + await this.enterText(this.enterNameInContainerTxt, name); } async goToContentInListViewWithName(contentName: string) { @@ -1028,8 +1023,7 @@ export class ContentUiHelper extends UiBaseLocators { } async enterDocumentBlueprintName(name: string) { - await this.documentBlueprintModalEnterNameTxt.clear(); - await this.documentBlueprintModalEnterNameTxt.fill(name); + await this.enterText(this.documentBlueprintModalEnterNameTxt, name); } async clickSaveDocumentBlueprintButton() { @@ -1250,9 +1244,7 @@ export class ContentUiHelper extends UiBaseLocators { async enterPropertyValue(propertyName: string, value: string) { const property = this.property.filter({hasText: propertyName}); - await expect(property).toBeVisible(); - await property.locator('input').clear(); - await property.locator('input').fill(value); + await this.enterText(property.locator('input'), value); } async doesBlockContainBlockInAreaWithName(blockWithAreaName: string, areaName: string, blockInAreaName: string, index: number = 0) { @@ -1475,14 +1467,12 @@ export class ContentUiHelper extends UiBaseLocators { async enterPublishTime(time: string, index: number = 0) { const publishAtTxt = this.documentScheduleModal.locator('.publish-date').nth(index).locator('uui-form-layout-item').first().locator('#input'); - await expect(publishAtTxt).toBeVisible(); - await publishAtTxt.fill(time); + await this.enterText(publishAtTxt, time); } async enterUnpublishTime(time: string, index: number = 0) { const unpublishAtTxt = this.documentScheduleModal.locator('.publish-date').nth(index).locator('uui-form-layout-item').last().locator('#input'); - await expect(unpublishAtTxt).toBeVisible(); - await unpublishAtTxt.fill(time); + await this.enterText(unpublishAtTxt, time); } async doesPublishAtValidationMessageContainText(text: string) { @@ -1615,20 +1605,17 @@ export class ContentUiHelper extends UiBaseLocators { } async enterSearchKeywordInTreePickerModal(keyword: string) { - await expect(this.treePickerSearchTxt).toBeVisible(); - await this.treePickerSearchTxt.fill(keyword); + await this.enterText(this.treePickerSearchTxt, keyword); await this.page.keyboard.press('Enter'); } async enterSearchKeywordInMediaPickerModal(keyword: string) { - await expect(this.mediaPickerSearchTxt).toBeVisible(); - await this.mediaPickerSearchTxt.fill(keyword); + await this.enterText(this.mediaPickerSearchTxt, keyword); await this.page.keyboard.press('Enter'); } async enterSearchKeywordInMemberPickerModal(keyword: string) { - await expect(this.memberPickerSearchTxt).toBeVisible(); - await this.memberPickerSearchTxt.fill(keyword); + await this.enterText(this.memberPickerSearchTxt, keyword); await this.page.keyboard.press('Enter'); } @@ -1736,9 +1723,7 @@ export class ContentUiHelper extends UiBaseLocators { async enterBlockPropertyValue(propertyName: string, value: string) { const property = this.blockProperty.filter({hasText: propertyName}); - await expect(property).toBeVisible(); - await property.locator('input').clear(); - await property.locator('input').fill(value); + await this.enterText(property.locator('input'), value); } async isBlockPropertyEditable(propertyName: string, isEditable: boolean = true) { From 038f47e08e5be7f9704dd393acf5ad1cc8231fa8 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 16:30:04 +0700 Subject: [PATCH 11/34] Refactor MediaUiHelper to use enterText method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- lib/helpers/MediaUiHelper.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/helpers/MediaUiHelper.ts b/lib/helpers/MediaUiHelper.ts index b183657c..f62adbfa 100644 --- a/lib/helpers/MediaUiHelper.ts +++ b/lib/helpers/MediaUiHelper.ts @@ -63,8 +63,7 @@ export class MediaUiHelper extends UiBaseLocators { } async searchForMediaItemByName(name: string) { - await this.mediaSearchTxt.clear(); - await this.mediaSearchTxt.fill(name); + await this.enterText(this.mediaSearchTxt, name); } async doesMediaCardsContainAmount(count: number) { From 655a4ca74dccdf1fc54685745c4791c8e06922fa Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 16:32:08 +0700 Subject: [PATCH 12/34] Refactor ContentUiHelper to use enterText method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace clear/fill patterns with enterText wrapper for enterContentName, enterTextstring, and enterTextArea methods. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- lib/helpers/ContentUiHelper.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index d9281341..fde84ea5 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -370,10 +370,7 @@ export class ContentUiHelper extends UiBaseLocators { } async enterContentName(name: string) { - await expect(this.contentNameTxt).toBeVisible(); - await this.contentNameTxt.clear(); - await this.contentNameTxt.fill(name); - await expect(this.contentNameTxt).toHaveValue(name); + await this.enterText(this.contentNameTxt, name, {verify: true}); } async clickSaveAndPublishButton() { @@ -439,9 +436,7 @@ export class ContentUiHelper extends UiBaseLocators { } async enterTextstring(text: string) { - await expect(this.textstringTxt).toBeVisible(); - await this.textstringTxt.clear(); - await this.textstringTxt.fill(text); + await this.enterText(this.textstringTxt, text); } async doesContentTreeHaveName(contentName: string) { @@ -454,10 +449,8 @@ export class ContentUiHelper extends UiBaseLocators { } async enterTextArea(value: string) { - await this.waitForVisible(this.textAreaTxt); await this.page.waitForTimeout(ConstantHelper.wait.minimal); - await this.textAreaTxt.clear(); - await this.textAreaTxt.fill(value); + await this.enterText(this.textAreaTxt, value); } async clickConfirmToUnpublishButton() { From b7cbe0eced713b6773c0d3dde9f65b66aeceed05 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 16:46:25 +0700 Subject: [PATCH 13/34] Refactor UI helpers to use waitForVisible from BasePage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace expect().toBeVisible() calls with waitForVisible wrapper method for consistency. Also refactor ExternalLoginUiHelpers to use click and enterText methods. Files updated: - ExternalLoginUiHelpers.ts - ContentUiHelper.ts - UserGroupUiHelper.ts - DictionaryUiHelper.ts - MemberGroupUiHelper.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- lib/helpers/ContentUiHelper.ts | 8 ++++---- lib/helpers/DictionaryUiHelper.ts | 2 +- lib/helpers/MemberGroupUiHelper.ts | 2 +- lib/helpers/UserGroupUiHelper.ts | 2 +- .../ExternalLoginUiHelpers.ts | 16 ++++++---------- 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index fde84ea5..7eb96320 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -444,7 +444,7 @@ export class ContentUiHelper extends UiBaseLocators { } async enterRichTextArea(value: string) { - await expect(this.richTextAreaTxt).toBeVisible(); + await this.waitForVisible(this.richTextAreaTxt); await this.richTextAreaTxt.fill(value); } @@ -844,7 +844,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesDocumentTableColumnNameValuesMatch(expectedValues: string[]) { - await expect(this.documentListView).toBeVisible(); + await this.waitForVisible(this.documentListView); return expectedValues.forEach((text, index) => { expect(this.documentTableColumnName.nth(index)).toHaveText(text); }); @@ -966,7 +966,7 @@ export class ContentUiHelper extends UiBaseLocators { } async isDocumentNameInputEditable(isEditable: boolean = true) { - await expect(this.contentNameTxt).toBeVisible(); + await this.waitForVisible(this.contentNameTxt); await expect(this.contentNameTxt).toBeEditable({editable: isEditable}); } @@ -1413,7 +1413,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesUploadedSvgThumbnailHaveSrc(imageSrc: string) { - await expect(this.uploadedSvgThumbnail).toBeVisible(); + await this.waitForVisible(this.uploadedSvgThumbnail); await expect(this.uploadedSvgThumbnail).toHaveAttribute('src', imageSrc); } diff --git a/lib/helpers/DictionaryUiHelper.ts b/lib/helpers/DictionaryUiHelper.ts index 5fff4084..f4a8d59b 100644 --- a/lib/helpers/DictionaryUiHelper.ts +++ b/lib/helpers/DictionaryUiHelper.ts @@ -78,7 +78,7 @@ export class DictionaryUiHelper extends UiBaseLocators { } async doesDictionaryListHaveText(text: string) { - await expect(this.dictionaryList).toBeVisible(); + await this.waitForVisible(this.dictionaryList); const allRows = await this.dictionaryListRows.all(); for (let currentRow of allRows) { const currentText = await currentRow.innerText(); diff --git a/lib/helpers/MemberGroupUiHelper.ts b/lib/helpers/MemberGroupUiHelper.ts index ee977168..cff1a66d 100644 --- a/lib/helpers/MemberGroupUiHelper.ts +++ b/lib/helpers/MemberGroupUiHelper.ts @@ -28,7 +28,7 @@ export class MemberGroupUiHelper extends UiBaseLocators { await this.waitForVisible(this.memberGroupsTab); await this.page.waitForTimeout(ConstantHelper.wait.short); await this.click(this.memberGroupsTab); - await expect(this.activeMemberGroupsTab).toBeVisible(); + await this.waitForVisible(this.activeMemberGroupsTab); } async clickMemberGroupCreateButton() { diff --git a/lib/helpers/UserGroupUiHelper.ts b/lib/helpers/UserGroupUiHelper.ts index 5881016a..d73ba2ea 100644 --- a/lib/helpers/UserGroupUiHelper.ts +++ b/lib/helpers/UserGroupUiHelper.ts @@ -135,7 +135,7 @@ export class UserGroupUiHelper extends UiBaseLocators { } async doesUserGroupContainLanguage(languageName: string, isVisible = true) { - await expect(this.languageInput).toBeVisible(); + await this.waitForVisible(this.languageInput); await expect(this.languageInput.filter({hasText: languageName})).toBeVisible({visible: isVisible}); } diff --git a/lib/helpers/differentAppSettingsHelpers/ExternalLoginUiHelpers.ts b/lib/helpers/differentAppSettingsHelpers/ExternalLoginUiHelpers.ts index 89bbcf48..a375cf72 100644 --- a/lib/helpers/differentAppSettingsHelpers/ExternalLoginUiHelpers.ts +++ b/lib/helpers/differentAppSettingsHelpers/ExternalLoginUiHelpers.ts @@ -1,4 +1,4 @@ -import {Page, Locator, expect} from "@playwright/test"; +import {Page, Locator} from "@playwright/test"; import {UiBaseLocators} from "../UiBaseLocators"; export class ExternalLoginUiHelpers extends UiBaseLocators { @@ -16,22 +16,18 @@ export class ExternalLoginUiHelpers extends UiBaseLocators { } async clickSignInWithAzureADB2CButton() { - await expect(this.azureADB2CSignInBtn).toBeVisible(); - await this.azureADB2CSignInBtn.click(); + await this.click(this.azureADB2CSignInBtn); } async enterAzureADB2CEmail(email: string) { - await expect(this.azureADB2CEmailTxt).toBeVisible(); - await this.azureADB2CEmailTxt.fill(email); + await this.enterText(this.azureADB2CEmailTxt, email); } async enterAzureADB2CPassword(password: string) { - await expect(this.azureADB2CPasswordTxt).toBeVisible(); - await this.azureADB2CPasswordTxt.fill(password); + await this.enterText(this.azureADB2CPasswordTxt, password); } async clickSignInButton() { - await expect(this.signInBtn).toBeVisible(); - await this.signInBtn.click(); + await this.click(this.signInBtn); } -} \ No newline at end of file +} From d1373cbd642bee01dd75aa56ecc8b9afca157a11 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 16:54:22 +0700 Subject: [PATCH 14/34] Refactor UI helpers to use hoverAndClick method from BasePage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hover + click patterns with the combined hoverAndClick wrapper method for cleaner code and consistent behavior when interacting with elements that appear on hover. Files updated: - ContentUiHelper.ts - DataTypeUiHelper.ts - FormsUiHelper.ts - LanguageUiHelper.ts - MediaUiHelper.ts - UiBaseLocators.ts - UserUiHelper.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- lib/helpers/ContentUiHelper.ts | 30 ++++++++++-------------------- lib/helpers/DataTypeUiHelper.ts | 18 ++++++------------ lib/helpers/FormsUiHelper.ts | 6 ++---- lib/helpers/LanguageUiHelper.ts | 4 ++-- lib/helpers/MediaUiHelper.ts | 3 +-- lib/helpers/UiBaseLocators.ts | 7 ++----- lib/helpers/UserUiHelper.ts | 4 ++-- 7 files changed, 25 insertions(+), 47 deletions(-) diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index 7eb96320..fc28f219 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -632,8 +632,7 @@ export class ContentUiHelper extends UiBaseLocators { async removeContentPicker(contentPickerName: string) { const contentPickerLocator = this.entityItem.filter({has: this.page.locator(`[name="${contentPickerName}"]`)}); - await this.hover(contentPickerLocator); - await this.click(contentPickerLocator.getByLabel('Remove')); + await this.hoverAndClick(contentPickerLocator, contentPickerLocator.getByLabel('Remove')); await this.clickConfirmRemoveButton(); } @@ -709,8 +708,7 @@ export class ContentUiHelper extends UiBaseLocators { async removeMemberPickerByName(memberName: string) { const mediaPickerLocator = this.entityItem.filter({has: this.page.locator(`[name="${memberName}"]`)}); - await this.hover(mediaPickerLocator); - await this.click(mediaPickerLocator.getByLabel('Remove')); + await this.hoverAndClick(mediaPickerLocator, mediaPickerLocator.getByLabel('Remove')); await this.clickConfirmRemoveButton(); } @@ -1170,36 +1168,30 @@ export class ContentUiHelper extends UiBaseLocators { } async clickEditBlockGridBlockButton() { - await this.hover(this.blockGridEntry); - await this.click(this.editBlockEntryBtn); + await this.hoverAndClick(this.blockGridEntry, this.editBlockEntryBtn); } async clickDeleteBlockGridBlockButton() { - await this.hover(this.blockGridEntry); - await this.click(this.deleteBlockEntryBtn); + await this.hoverAndClick(this.blockGridEntry, this.deleteBlockEntryBtn); } async clickEditBlockListBlockButton() { - await this.hover(this.blockListEntry); - await this.click(this.editBlockEntryBtn); + await this.hoverAndClick(this.blockListEntry, this.editBlockEntryBtn); } async clickDeleteBlockListBlockButton() { - await this.hover(this.blockListEntry); - await this.click(this.deleteBlockEntryBtn); + await this.hoverAndClick(this.blockListEntry, this.deleteBlockEntryBtn); } async clickCopyBlockListBlockButton(groupName: string, propertyName: string, blockName: string, index: number = 0) { const blockListBlock = this.workspaceEditTab.filter({hasText: groupName}).locator(this.workspaceEditProperties).filter({hasText: propertyName}).locator(this.blockListEntry).nth(index).filter({hasText: blockName}); - await this.hover(blockListBlock); - await this.click(blockListBlock.locator(this.copyBlockEntryBtn), {force: true}); + await this.hoverAndClick(blockListBlock, blockListBlock.locator(this.copyBlockEntryBtn), {force: true}); await this.page.waitForTimeout(ConstantHelper.wait.short); } async clickCopyBlockGridBlockButton(groupName: string, propertyName: string, blockName: string, index: number = 0) { const blockGridBlock = this.workspaceEditTab.filter({hasText: groupName}).locator(this.workspaceEditProperties).filter({hasText: propertyName}).locator(this.blockGridEntry).nth(index).filter({hasText: blockName}); - await this.hover(blockGridBlock); - await this.click(blockGridBlock.locator(this.copyBlockEntryBtn), {force: true}); + await this.hoverAndClick(blockGridBlock, blockGridBlock.locator(this.copyBlockEntryBtn), {force: true}); await this.page.waitForTimeout(ConstantHelper.wait.short); } @@ -1211,8 +1203,7 @@ export class ContentUiHelper extends UiBaseLocators { async clickActionsMenuForProperty(groupName: string, propertyName: string) { const property = this.workspaceEditTab.filter({hasText: groupName}).locator(this.workspaceEditProperties).filter({hasText: propertyName}); - await this.hover(property); - await this.click(property.locator(this.openActionsMenu), {force: true}); + await this.hoverAndClick(property, property.locator(this.openActionsMenu), {force: true}); } async clickAddBlockGridElementWithName(elementTypeName: string) { @@ -1282,8 +1273,7 @@ export class ContentUiHelper extends UiBaseLocators { const parentBlock = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: parentBlockName})).nth(parentIndex); const area = parentBlock.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); const block = area.locator(this.blockGridEntry.filter({hasText: blockName})).nth(childIndex); - await this.hover(block); - await this.click(block.getByLabel('delete'), {force: true}); + await this.hoverAndClick(block, block.getByLabel('delete'), {force: true}); } async doesBlockAreaContainColumnSpan(blockWithAreaName: string, areaName: string, columnSpan: number, index: number = 0) { diff --git a/lib/helpers/DataTypeUiHelper.ts b/lib/helpers/DataTypeUiHelper.ts index 788c340a..08745c4b 100644 --- a/lib/helpers/DataTypeUiHelper.ts +++ b/lib/helpers/DataTypeUiHelper.ts @@ -743,8 +743,7 @@ export class DataTypeUiHelper extends UiBaseLocators { async removeContentStartNode(contentName: string) { const startNodeLocator = this.entityItem.filter({has: this.page.locator(`[name="${contentName}"]`)}); - await this.hover(startNodeLocator); - await this.click(startNodeLocator.getByLabel('Remove')); + await this.hoverAndClick(startNodeLocator, startNodeLocator.getByLabel('Remove')); await this.clickConfirmRemoveButton(); } @@ -853,23 +852,19 @@ export class DataTypeUiHelper extends UiBaseLocators { } async removeBlockContentModel() { - await this.hover(this.contentModelNode); - await this.click(this.removeExactContentModelNodeBtn); + await this.hoverAndClick(this.contentModelNode, this.removeExactContentModelNodeBtn); } async removeBlockSettingsModel() { - await this.hover(this.settingsModelNode); - await this.click(this.removeExactSettingsModelNodeBtn); + await this.hoverAndClick(this.settingsModelNode, this.removeExactSettingsModelNodeBtn); } async openBlockContentModel() { - await this.hover(this.contentModelNode); - await this.click(this.openBtn); + await this.hoverAndClick(this.contentModelNode, this.openBtn); } async openBlockSettingsModel() { - await this.hover(this.settingsModelNode); - await this.click(this.openBtn); + await this.hoverAndClick(this.settingsModelNode, this.openBtn); } async isElementWorkspaceOpenInBlock(elementTypeName: string) { @@ -1075,8 +1070,7 @@ export class DataTypeUiHelper extends UiBaseLocators { async deleteToolbarRow(rowIndex: number) { const rowButton = this.tiptapToolbarConfiguration.locator('.row').nth(rowIndex); - await this.hover(rowButton); - await this.click(rowButton.locator('[label="Remove row"]')); + await this.hoverAndClick(rowButton, rowButton.locator('[label="Remove row"]')); } async clickAddRowToolbarButton() { diff --git a/lib/helpers/FormsUiHelper.ts b/lib/helpers/FormsUiHelper.ts index 48202335..03fa14ae 100644 --- a/lib/helpers/FormsUiHelper.ts +++ b/lib/helpers/FormsUiHelper.ts @@ -137,8 +137,7 @@ export class FormsUiHelper extends UiBaseLocators { */ async clickQuickCreateFormButton() { - await this.hover(this.formMenuItemForForm); - await this.click(this.formMenuItemForForm.locator(this.quickCreateNewBtn)); + await this.hoverAndClick(this.formMenuItemForForm, this.formMenuItemForForm.locator(this.quickCreateNewBtn)); } async clickNewFormButton() { @@ -468,8 +467,7 @@ export class FormsUiHelper extends UiBaseLocators { */ async clickQuickCreatePrevalueSourceButton() { - await this.hover(this.formMenuItemForPrevalueSource); - await this.click(this.formMenuItemForPrevalueSource.locator(this.quickCreateNewBtn)); + await this.hoverAndClick(this.formMenuItemForPrevalueSource, this.formMenuItemForPrevalueSource.locator(this.quickCreateNewBtn)); } async clickPrevalueSourceTypeButton(type: string) { diff --git a/lib/helpers/LanguageUiHelper.ts b/lib/helpers/LanguageUiHelper.ts index 0e6b7f49..e0c4828d 100644 --- a/lib/helpers/LanguageUiHelper.ts +++ b/lib/helpers/LanguageUiHelper.ts @@ -48,8 +48,8 @@ export class LanguageUiHelper extends UiBaseLocators { } async removeFallbackLanguageByIsoCode(isoCode: string) { - await this.hover(this.page.locator(`umb-entity-item-ref[id="${isoCode}"]`)); - await this.click(this.page.locator(`umb-entity-item-ref[id="${isoCode}"]`).getByLabel('Remove')); + const languageLocator = this.page.locator(`umb-entity-item-ref[id="${isoCode}"]`); + await this.hoverAndClick(languageLocator, languageLocator.getByLabel('Remove')); await this.click(this.confirmToRemoveBtn); } diff --git a/lib/helpers/MediaUiHelper.ts b/lib/helpers/MediaUiHelper.ts index f62adbfa..646116e6 100644 --- a/lib/helpers/MediaUiHelper.ts +++ b/lib/helpers/MediaUiHelper.ts @@ -121,9 +121,8 @@ export class MediaUiHelper extends UiBaseLocators { } async clickEmptyRecycleBinButton() { - await this.hover(this.recycleBinMenuItem); // Force click is needed - await this.click(this.emptyRecycleBinBtn, {force: true}); + await this.hoverAndClick(this.recycleBinMenuItem, this.emptyRecycleBinBtn, {force: true}); } async clickConfirmEmptyRecycleBinButton() { diff --git a/lib/helpers/UiBaseLocators.ts b/lib/helpers/UiBaseLocators.ts index f38a91bc..b7c05e5c 100644 --- a/lib/helpers/UiBaseLocators.ts +++ b/lib/helpers/UiBaseLocators.ts @@ -916,8 +916,7 @@ export class UiBaseLocators extends BasePage { async deletePropertyEditorWithName(name: string) { const propertyEditor = this.page.locator('umb-content-type-design-editor-property', {hasText: name}); - await this.hover(propertyEditor); - await this.click(propertyEditor.getByLabel('Delete'), {force: true}); + await this.hoverAndClick(propertyEditor, propertyEditor.getByLabel('Delete'), {force: true}); await this.clickConfirmToDeleteButton(); } @@ -996,9 +995,7 @@ export class UiBaseLocators extends BasePage { async clickRemoveTabWithName(name: string) { const tab = this.page.locator('uui-tab').filter({hasText: name}); - await this.hover(tab); - const removeTabWithNameLocator = tab.locator('[label="Remove"]'); - await this.click(removeTabWithNameLocator); + await this.hoverAndClick(tab, tab.locator('[label="Remove"]')); } async clickStructureTab() { diff --git a/lib/helpers/UserUiHelper.ts b/lib/helpers/UserUiHelper.ts index 3b7e07c1..ac52a784 100644 --- a/lib/helpers/UserUiHelper.ts +++ b/lib/helpers/UserUiHelper.ts @@ -226,8 +226,8 @@ export class UserUiHelper extends UiBaseLocators { } async clickRemoveButtonForContentNodeWithName(name: string) { - await this.hover(this.entityItem.filter({has: this.page.locator(`[name="${name}"]`)})); - await this.click(this.entityItem.filter({has: this.page.locator(`[name="${name}"]`)}).getByRole('button', {name: 'Remove'}), {force: true}); + const entityItemLocator = this.entityItem.filter({has: this.page.locator(`[name="${name}"]`)}); + await this.hoverAndClick(entityItemLocator, entityItemLocator.getByRole('button', {name: 'Remove'}), {force: true}); } async clickRemoveButtonForMediaNodeWithName(name: string) { From e524dc620161d724ad98977bfd973be4064bf31d Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 17:05:56 +0700 Subject: [PATCH 15/34] Refactor UI helpers to use BasePage wrapper methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace this.page.waitForLoadState() with this.waitForLoadState() - Replace locator.press() with this.pressKey() for locator-based key presses - Replace selectOption calls with selectByText/selectByValue/selectMultiple - Simplify enterText patterns that use clear() before fill() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- lib/helpers/ContentUiHelper.ts | 56 +++++++++---------- lib/helpers/DataTypeUiHelper.ts | 18 +++--- lib/helpers/DictionaryUiHelper.ts | 8 +-- lib/helpers/DocumentBlueprintUiHelper.ts | 4 +- lib/helpers/DocumentTypeUiHelper.ts | 6 +- lib/helpers/FormsUiHelper.ts | 12 ++-- lib/helpers/LanguageUiHelper.ts | 4 +- lib/helpers/MediaTypeUiHelper.ts | 6 +- lib/helpers/MediaUiHelper.ts | 8 +-- lib/helpers/MemberGroupUiHelper.ts | 4 +- lib/helpers/MemberUiHelper.ts | 4 +- lib/helpers/PartialViewUiHelper.ts | 6 +- lib/helpers/RelationTypeUiHelper.ts | 4 +- lib/helpers/ScriptUiHelper.ts | 6 +- lib/helpers/StylesheetUiHelper.ts | 6 +- lib/helpers/TemplateUiHelper.ts | 6 +- lib/helpers/UiBaseLocators.ts | 4 +- lib/helpers/UserGroupUiHelper.ts | 6 +- lib/helpers/UserUiHelper.ts | 6 +- .../InstallUiHelper.ts | 2 +- 20 files changed, 86 insertions(+), 90 deletions(-) diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index fc28f219..a6423c99 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -495,31 +495,31 @@ export class ContentUiHelper extends UiBaseLocators { } async waitForContentToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForContentToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForContentToBeRenamed() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForDomainToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForDomainToBeUpdated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForDomainToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForContentToBeTrashed() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async clickDocumentTypeByName(documentTypeName: string) { @@ -638,7 +638,7 @@ export class ContentUiHelper extends UiBaseLocators { // Dropdown async chooseDropdownOption(optionValues: string[]) { - await this.dropdown.selectOption(optionValues); + await this.selectMultiple(this.dropdown, optionValues); } // Date Picker @@ -728,8 +728,8 @@ export class ContentUiHelper extends UiBaseLocators { } async enterTag(tagName: string) { - await this.enterTagTxt.fill(tagName); - await this.enterTagTxt.press('Enter'); + await this.enterText(this.enterTagTxt, tagName); + await this.pressKey(this.enterTagTxt, 'Enter'); } async removeTagByName(tagName: string) { @@ -746,29 +746,29 @@ export class ContentUiHelper extends UiBaseLocators { } async enterLink(value: string, toPress: boolean = false) { - await this.linkTxt.clear(); if (toPress) { - await this.linkTxt.press(value); + await this.enterText(this.linkTxt, ''); + await this.pressKey(this.linkTxt, value); } else { - await this.linkTxt.fill(value); + await this.enterText(this.linkTxt, value); } } async enterAnchorOrQuerystring(value: string, toPress: boolean = false) { - await this.anchorQuerystringTxt.clear(); if (toPress) { - await this.anchorQuerystringTxt.press(value); + await this.enterText(this.anchorQuerystringTxt, ''); + await this.pressKey(this.anchorQuerystringTxt, value); } else { - await this.anchorQuerystringTxt.fill(value); + await this.enterText(this.anchorQuerystringTxt, value); } } async enterLinkTitle(value: string, toPress: boolean = false) { - await this.linkTitleTxt.clear(); if (toPress) { - await this.linkTitleTxt.press(value); + await this.enterText(this.linkTitleTxt, ''); + await this.pressKey(this.linkTitleTxt, value); } else { - await this.linkTitleTxt.fill(value); + await this.enterText(this.linkTitleTxt, value); } } @@ -850,7 +850,7 @@ export class ContentUiHelper extends UiBaseLocators { async searchByKeywordInCollection(keyword: string) { await this.enterText(this.searchTxt, keyword); - await this.searchTxt.press('Enter'); + await this.pressKey(this.searchTxt, 'Enter'); await this.page.waitForTimeout(ConstantHelper.wait.short); } @@ -1559,23 +1559,23 @@ export class ContentUiHelper extends UiBaseLocators { async selectAllRTETipTapEditorText() { await this.click(this.tipTapEditor); - await this.page.keyboard.press('Control+A'); + await this.pressKey(this.tipTapEditor, 'Control+A'); } async waitForContentToBePublished() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForRecycleBinToBeEmptied() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async clearTipTapEditor() { await this.waitForVisible(this.tipTapEditor); // We use the middle mouse button click so we don't accidentally open a block in the RTE. This solution avoids that. await this.tipTapEditor.click({button: "middle"}); - await this.page.keyboard.press('Control+A'); - await this.page.keyboard.press('Backspace'); + await this.pressKey(this.tipTapEditor, 'Control+A'); + await this.pressKey(this.tipTapEditor, 'Backspace'); } async clickBlockElementInRTEWithName(elementTypeName: string) { @@ -1589,17 +1589,17 @@ export class ContentUiHelper extends UiBaseLocators { async enterSearchKeywordInTreePickerModal(keyword: string) { await this.enterText(this.treePickerSearchTxt, keyword); - await this.page.keyboard.press('Enter'); + await this.pressKey(this.treePickerSearchTxt, 'Enter'); } async enterSearchKeywordInMediaPickerModal(keyword: string) { await this.enterText(this.mediaPickerSearchTxt, keyword); - await this.page.keyboard.press('Enter'); + await this.pressKey(this.mediaPickerSearchTxt, 'Enter'); } async enterSearchKeywordInMemberPickerModal(keyword: string) { await this.enterText(this.memberPickerSearchTxt, keyword); - await this.page.keyboard.press('Enter'); + await this.pressKey(this.memberPickerSearchTxt, 'Enter'); } async isContentNameReadOnly() { diff --git a/lib/helpers/DataTypeUiHelper.ts b/lib/helpers/DataTypeUiHelper.ts index 08745c4b..2be1870c 100644 --- a/lib/helpers/DataTypeUiHelper.ts +++ b/lib/helpers/DataTypeUiHelper.ts @@ -357,7 +357,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async waitForDataTypeToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async isDataTypeTreeItemVisible(name: string, isVisible: boolean = true) { @@ -371,11 +371,11 @@ export class DataTypeUiHelper extends UiBaseLocators { } async waitForDataTypeToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForDataTypeToBeRenamed() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async clickNewDataTypeButton() { @@ -458,7 +458,7 @@ export class DataTypeUiHelper extends UiBaseLocators { // Label async changeValueType(valueType: string) { - await this.page.getByLabel('Select a value type').selectOption({label: valueType}); + await this.selectByText(this.page.getByLabel('Select a value type'), valueType); } // Date Picker @@ -505,7 +505,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async chooseOrderByValue(value: string) { - await this.orderByDropDownBox.selectOption({label: value}); + await this.selectByText(this.orderByDropDownBox, value); } async enterWorkspaceViewName(name: string) { @@ -634,7 +634,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async chooseOverlaySizeByValue(value: string) { - await this.overlaySizeDropDownBox.selectOption({value: value}); + await this.selectByValue(this.overlaySizeDropDownBox, value); } async clickHideAnchorQueryStringInputToggle() { @@ -732,8 +732,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async selectStorageTypeOption(option: string) { - await this.waitForVisible(this.storageTypeDropDownBox); - await this.storageTypeDropDownBox.selectOption({label: option}); + await this.selectByText(this.storageTypeDropDownBox, option); } // Content Picker @@ -835,8 +834,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async updateBlockOverlaySize(size: string) { - await this.waitForVisible(this.overlaySizeOption); - await this.overlaySizeOption.selectOption(size); + await this.selectByValue(this.overlaySizeOption, size); } async addBlockContentModel(elementName: string) { diff --git a/lib/helpers/DictionaryUiHelper.ts b/lib/helpers/DictionaryUiHelper.ts index f4a8d59b..ea866ff5 100644 --- a/lib/helpers/DictionaryUiHelper.ts +++ b/lib/helpers/DictionaryUiHelper.ts @@ -49,7 +49,7 @@ export class DictionaryUiHelper extends UiBaseLocators { async enterSearchKeywordAndPressEnter(keyword: string) { await this.enterText(this.searchTxt, keyword); - await this.page.keyboard.press('Enter'); + await this.pressKey(this.searchTxt, 'Enter'); } async clickExportButton() { @@ -61,15 +61,15 @@ export class DictionaryUiHelper extends UiBaseLocators { } async waitForDictionaryToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForDictionaryToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForDictionaryToBeImported() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async deleteDictionary() { diff --git a/lib/helpers/DocumentBlueprintUiHelper.ts b/lib/helpers/DocumentBlueprintUiHelper.ts index 200a8876..81efa4bd 100644 --- a/lib/helpers/DocumentBlueprintUiHelper.ts +++ b/lib/helpers/DocumentBlueprintUiHelper.ts @@ -27,11 +27,11 @@ export class DocumentBlueprintUiHelper extends UiBaseLocators{ } async waitForDocumentBlueprintToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForDocumentBlueprintToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async reloadDocumentBlueprintsTree() { diff --git a/lib/helpers/DocumentTypeUiHelper.ts b/lib/helpers/DocumentTypeUiHelper.ts index d3a46067..67092a13 100644 --- a/lib/helpers/DocumentTypeUiHelper.ts +++ b/lib/helpers/DocumentTypeUiHelper.ts @@ -83,15 +83,15 @@ export class DocumentTypeUiHelper extends UiBaseLocators { } async waitForDocumentTypeToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForDocumentTypeToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForDocumentTypeToBeRenamed() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async enterDocumentTypeName(documentTypeName: string) { diff --git a/lib/helpers/FormsUiHelper.ts b/lib/helpers/FormsUiHelper.ts index 03fa14ae..82e282c4 100644 --- a/lib/helpers/FormsUiHelper.ts +++ b/lib/helpers/FormsUiHelper.ts @@ -272,8 +272,7 @@ export class FormsUiHelper extends UiBaseLocators { await this.click(displayDefaultFieldsToggleInput); await this.page.waitForTimeout(ConstantHelper.wait.minimal); // short pause required here otherwise revealed elements are not found const displayFieldsSelect = this.formSettingFieldsDisplayed.locator("select"); - await this.waitForVisible(displayFieldsSelect); - await displayFieldsSelect.selectOption({ value: '_system_state' }); + await this.selectByValue(displayFieldsSelect, '_system_state'); const displayFieldsAddButton = this.formSettingFieldsDisplayed.locator("button[id='button']"); await this.click(displayFieldsAddButton); } @@ -299,7 +298,7 @@ export class FormsUiHelper extends UiBaseLocators { async applyFieldSettingViaDropDown(settingAlias: string, settingValue: string) { const settingFieldLocator = this.page.locator(`umb-property[alias="${settingAlias}"] select`); - await settingFieldLocator.selectOption({ value: settingValue }); + await this.selectByValue(settingFieldLocator, settingValue); } async applyFieldSettingViaSlider(settingAlias: string) { @@ -385,7 +384,7 @@ export class FormsUiHelper extends UiBaseLocators { await this.waitForVisible(settingFieldLocator); const selectLocator = settingFieldLocator.locator("forms-document-mapper-property-editor select"); - await selectLocator.selectOption({ value : settingValue.doctype }); + await this.selectByValue(selectLocator, settingValue.doctype); const inputLocator = settingFieldLocator.locator('forms-document-mapper-property-editor input[type = "text"]'); await this.waitForVisible(inputLocator.first()); @@ -432,7 +431,7 @@ export class FormsUiHelper extends UiBaseLocators { async setFieldValidation(label: string, message: string) { await this.waitForVisible(this.formFieldRegex); const selectLocator = this.formFieldRegex.locator("select"); - await selectLocator.selectOption({ label: label }); + await this.selectByText(selectLocator, label); await this.page.waitForTimeout(ConstantHelper.wait.medium); const inputLocator = this.formFieldRegex.locator("input"); await this.enterText(inputLocator, message); @@ -500,8 +499,7 @@ export class FormsUiHelper extends UiBaseLocators { await numberInput.fill(timeValue.toString()); const unitSelect = this.prevalueSourceCacheContainer.locator("select"); - await this.waitForVisible(unitSelect); - await unitSelect.selectOption({ value: timeUnit }); + await this.selectByValue(unitSelect, timeUnit); } } diff --git a/lib/helpers/LanguageUiHelper.ts b/lib/helpers/LanguageUiHelper.ts index e0c4828d..45b2bf0b 100644 --- a/lib/helpers/LanguageUiHelper.ts +++ b/lib/helpers/LanguageUiHelper.ts @@ -40,11 +40,11 @@ export class LanguageUiHelper extends UiBaseLocators { } async waitForLanguageToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForLanguageToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async removeFallbackLanguageByIsoCode(isoCode: string) { diff --git a/lib/helpers/MediaTypeUiHelper.ts b/lib/helpers/MediaTypeUiHelper.ts index 8942c15c..290c495f 100644 --- a/lib/helpers/MediaTypeUiHelper.ts +++ b/lib/helpers/MediaTypeUiHelper.ts @@ -44,15 +44,15 @@ export class MediaTypeUiHelper extends UiBaseLocators { } async waitForMediaTypeToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForMediaTypeToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForMediaTypeToBeRenamed() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } diff --git a/lib/helpers/MediaUiHelper.ts b/lib/helpers/MediaUiHelper.ts index 646116e6..396568a0 100644 --- a/lib/helpers/MediaUiHelper.ts +++ b/lib/helpers/MediaUiHelper.ts @@ -86,19 +86,19 @@ export class MediaUiHelper extends UiBaseLocators { } async waitForMediaToBeTrashed() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForRecycleBinToBeEmptied() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForMediaToBeMoved() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForMediaItemToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async deleteMediaItem(name: string) { diff --git a/lib/helpers/MemberGroupUiHelper.ts b/lib/helpers/MemberGroupUiHelper.ts index cff1a66d..a557471c 100644 --- a/lib/helpers/MemberGroupUiHelper.ts +++ b/lib/helpers/MemberGroupUiHelper.ts @@ -56,11 +56,11 @@ export class MemberGroupUiHelper extends UiBaseLocators { } async waitForMemberGroupToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForMemberGroupToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async goToMemberGroups() { diff --git a/lib/helpers/MemberUiHelper.ts b/lib/helpers/MemberUiHelper.ts index 88424554..bc71d908 100644 --- a/lib/helpers/MemberUiHelper.ts +++ b/lib/helpers/MemberUiHelper.ts @@ -139,11 +139,11 @@ export class MemberUiHelper extends UiBaseLocators { } async waitForMemberToBeCreated(){ - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForMemberToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async goToMembers() { diff --git a/lib/helpers/PartialViewUiHelper.ts b/lib/helpers/PartialViewUiHelper.ts index 0b9a2b6a..87496801 100644 --- a/lib/helpers/PartialViewUiHelper.ts +++ b/lib/helpers/PartialViewUiHelper.ts @@ -33,15 +33,15 @@ export class PartialViewUiHelper extends UiBaseLocators { } async waitForPartialViewToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForPartialViewToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForPartialViewToBeRenamed() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async clickNewEmptyPartialViewButton() { diff --git a/lib/helpers/RelationTypeUiHelper.ts b/lib/helpers/RelationTypeUiHelper.ts index 19e6aeb9..df65c797 100644 --- a/lib/helpers/RelationTypeUiHelper.ts +++ b/lib/helpers/RelationTypeUiHelper.ts @@ -92,10 +92,10 @@ export class RelationTypeUiHelper extends UiBaseLocators{ } async selectParentOption(option: string) { - await this.parentDropDownBox.selectOption({ label: option }); + await this.selectByText(this.parentDropDownBox, option); } async selectChildOption(option: string) { - await this.childDropDownBox.selectOption({ label: option }); + await this.selectByText(this.childDropDownBox, option); } } \ No newline at end of file diff --git a/lib/helpers/ScriptUiHelper.ts b/lib/helpers/ScriptUiHelper.ts index f86e1ddc..a3494f7b 100644 --- a/lib/helpers/ScriptUiHelper.ts +++ b/lib/helpers/ScriptUiHelper.ts @@ -40,15 +40,15 @@ export class ScriptUiHelper extends UiBaseLocators{ } async waitForScriptToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForScriptToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForScriptToBeRenamed() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } // Will only work for root scripts diff --git a/lib/helpers/StylesheetUiHelper.ts b/lib/helpers/StylesheetUiHelper.ts index 82109c53..e214acb6 100644 --- a/lib/helpers/StylesheetUiHelper.ts +++ b/lib/helpers/StylesheetUiHelper.ts @@ -46,15 +46,15 @@ export class StylesheetUiHelper extends UiBaseLocators{ } async waitForStylesheetToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForStylesheetToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForStylesheetToBeRenamed() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async enterStylesheetName(stylesheetName: string) { diff --git a/lib/helpers/TemplateUiHelper.ts b/lib/helpers/TemplateUiHelper.ts index 538d8e5c..0f1da7de 100644 --- a/lib/helpers/TemplateUiHelper.ts +++ b/lib/helpers/TemplateUiHelper.ts @@ -31,15 +31,15 @@ export class TemplateUiHelper extends UiBaseLocators { } async waitForTemplateToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForTemplateToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForTemplateToBeRenamed() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async goToTemplate(templateName: string, childTemplateName: string = '') { diff --git a/lib/helpers/UiBaseLocators.ts b/lib/helpers/UiBaseLocators.ts index b7c05e5c..3a89a1d1 100644 --- a/lib/helpers/UiBaseLocators.ts +++ b/lib/helpers/UiBaseLocators.ts @@ -1472,8 +1472,8 @@ export class UiBaseLocators extends BasePage { // Editor Methods async enterMonacoEditorValue(value: string) { await this.click(this.monacoEditor); - await this.page.keyboard.press('Control+A'); - await this.page.keyboard.press('Backspace'); + await this.pressKey(this.monacoEditor, 'Control+A'); + await this.pressKey(this.monacoEditor, 'Backspace'); await this.page.keyboard.insertText(value); } diff --git a/lib/helpers/UserGroupUiHelper.ts b/lib/helpers/UserGroupUiHelper.ts index d73ba2ea..d0d85270 100644 --- a/lib/helpers/UserGroupUiHelper.ts +++ b/lib/helpers/UserGroupUiHelper.ts @@ -72,15 +72,15 @@ export class UserGroupUiHelper extends UiBaseLocators { } async waitForUserGroupToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForUserGroupToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForUserGroupToBeRenamed() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async clickCreateUserGroupButton() { diff --git a/lib/helpers/UserUiHelper.ts b/lib/helpers/UserUiHelper.ts index ac52a784..5757db51 100644 --- a/lib/helpers/UserUiHelper.ts +++ b/lib/helpers/UserUiHelper.ts @@ -89,15 +89,15 @@ export class UserUiHelper extends UiBaseLocators { } async waitForUserToBeCreated() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForUserToBeDeleted() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async waitForUserToBeRenamed() { - await this.page.waitForLoadState(); + await this.waitForLoadState(); } async clickAddUserGroupsButton() { diff --git a/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts b/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts index 5a9093a2..bd191438 100644 --- a/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts +++ b/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts @@ -36,7 +36,7 @@ export class InstallUiHelper extends UiBaseLocators { } async setDatabaseType(databaseType: string) { - await this.databaseTypeInput.selectOption(databaseType); + await this.selectByValue(this.databaseTypeInput, databaseType); } async doesDatabaseHaveType(databaseType: string) { From 9cc4012d540de5da9410c538eb11c19cbfa290b4 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 17:19:55 +0700 Subject: [PATCH 16/34] Refactor UI helpers to use BasePage assertion methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace expect().toBeVisible() with isVisible()/waitForVisible() - Replace expect().toHaveText() with hasText() - Replace expect().toContainText() with containsText() - Replace expect().toHaveValue() with hasValue() - Remove unused expect imports 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- lib/helpers/ContentRenderUiHelper.ts | 6 +- lib/helpers/ContentUiHelper.ts | 78 +++++++++---------- lib/helpers/DataTypeUiHelper.ts | 2 +- lib/helpers/DictionaryUiHelper.ts | 4 +- lib/helpers/ExamineManagementUiHelper.ts | 2 +- lib/helpers/FormsUiHelper.ts | 4 +- lib/helpers/LogViewerUiHelper.ts | 4 +- lib/helpers/MediaUiHelper.ts | 8 +- lib/helpers/ModelsBuilderUiHelper.ts | 4 +- lib/helpers/PackageUiHelper.ts | 4 +- lib/helpers/PartialViewUiHelper.ts | 4 +- lib/helpers/RelationTypeUiHelper.ts | 8 +- lib/helpers/TelemetryDataUiHelper.ts | 4 +- lib/helpers/TemplateUiHelper.ts | 4 +- lib/helpers/UserGroupUiHelper.ts | 2 +- lib/helpers/UserUiHelper.ts | 8 +- .../InstallUiHelper.ts | 4 +- 17 files changed, 75 insertions(+), 75 deletions(-) diff --git a/lib/helpers/ContentRenderUiHelper.ts b/lib/helpers/ContentRenderUiHelper.ts index 3bcd97b1..5ecfd72f 100644 --- a/lib/helpers/ContentRenderUiHelper.ts +++ b/lib/helpers/ContentRenderUiHelper.ts @@ -18,9 +18,9 @@ export class ContentRenderUiHelper extends UiBaseLocators { async doesContentRenderValueContainText(text: string, isEqual: boolean = false) { if (isEqual) { - return await expect(this.contentRenderValue).toHaveText(text); + await this.hasText(this.contentRenderValue, text); } else { - return await expect(this.contentRenderValue).toContainText(text); + await this.containsText(this.contentRenderValue, text); } } @@ -34,6 +34,6 @@ export class ContentRenderUiHelper extends UiBaseLocators { } async doesDataSourceRenderValueHaveText(text: string) { - return await expect(this.dataSourceRenderValue).toHaveText(text); + await this.hasText(this.dataSourceRenderValue, text); } } \ No newline at end of file diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index a6423c99..9dc9277f 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -440,7 +440,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesContentTreeHaveName(contentName: string) { - await expect(this.contentTree).toContainText(contentName); + await this.containsText(this.contentTree, contentName); } async enterRichTextArea(value: string) { @@ -467,23 +467,23 @@ export class ContentUiHelper extends UiBaseLocators { } async doesDocumentHaveLink(link: string) { - await expect(this.linkContent).toContainText(link); + await this.containsText(this.linkContent, link); } async doesHistoryHaveText(text: string) { - await expect(this.historyItems).toHaveText(text); + await this.hasText(this.historyItems, text); } async doesDocumentStateHaveText(text: string) { - await expect(this.documentState).toHaveText(text); + await this.hasText(this.documentState, text); } async doesCreatedDateHaveText(text: string) { - await expect(this.createdDate).toHaveText(text); + await this.hasText(this.createdDate, text); } async doesIdHaveText(text: string) { - await expect(this.id).toHaveText(text); + await this.hasText(this.id, text); } async clickEditDocumentTypeButton() { @@ -792,7 +792,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesToggleHaveLabel(label: string) { - return await expect(this.toggleInput).toHaveText(label); + await this.hasText(this.toggleInput, label); } // Multiple Text String @@ -829,7 +829,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesModalHaveText(text: string) { - return expect(this.openedModal).toContainText(text); + await this.containsText(this.openedModal, text); } // Collection tab @@ -838,7 +838,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesDocumentHaveName(name: string) { - return expect(this.enterAName).toHaveValue(name); + await this.hasValue(this.enterAName, name); } async doesDocumentTableColumnNameValuesMatch(expectedValues: string[]) { @@ -942,11 +942,11 @@ export class ContentUiHelper extends UiBaseLocators { } async isDocumentListViewVisible(isVisible: boolean = true) { - await expect(this.documentListView).toBeVisible({visible: isVisible}); + await this.isVisible(this.documentListView, isVisible); } async isDocumentGridViewVisible(isVisible: boolean = true) { - await expect(this.documentGridView).toBeVisible({visible: isVisible}); + await this.isVisible(this.documentGridView, isVisible); } async changeDocumentSectionLanguage(newLanguageName: string) { @@ -956,11 +956,11 @@ export class ContentUiHelper extends UiBaseLocators { } async doesDocumentSectionHaveLanguageSelected(languageName: string) { - await expect(this.documentLanguageSelect).toHaveText(languageName); + await this.hasText(this.documentLanguageSelect, languageName); } async isDocumentReadOnly(isVisible: boolean = true) { - await expect(this.documentReadOnly).toBeVisible({visible: isVisible}); + await this.isVisible(this.documentReadOnly, isVisible); } async isDocumentNameInputEditable(isEditable: boolean = true) { @@ -988,13 +988,13 @@ export class ContentUiHelper extends UiBaseLocators { async isDocumentPropertyEditable(propertyName: string, isEditable: boolean = true) { const propertyLocator = this.documentWorkspace.locator(this.property).filter({hasText: propertyName}).locator('#input'); - await expect(propertyLocator).toBeVisible(); + await this.waitForVisible(propertyLocator); await expect(propertyLocator).toBeEditable({editable: isEditable}); } async doesDocumentPropertyHaveValue(propertyName: string, value: string) { const propertyLocator = this.documentWorkspace.locator(this.property).filter({hasText: propertyName}).locator('#input'); - await expect(propertyLocator).toHaveValue(value); + await this.hasValue(propertyLocator, value); } async clickContentTab() { @@ -1010,7 +1010,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesDocumentWorkspaceHaveText(text: string) { - return expect(this.documentWorkspace).toContainText(text); + await this.containsText(this.documentWorkspace, text); } async enterDocumentBlueprintName(name: string) { @@ -1148,19 +1148,19 @@ export class ContentUiHelper extends UiBaseLocators { } async isAddBlockElementButtonVisible(isVisible: boolean = true) { - await expect(this.addBlockElementBtn).toBeVisible({visible: isVisible}); + await this.isVisible(this.addBlockElementBtn, isVisible); } async isAddBlockElementButtonWithLabelVisible(blockName: string, label: string, isVisible: boolean = true) { - await expect(this.property.filter({hasText: blockName}).locator(this.addBlockElementBtn).filter({hasText: label})).toBeVisible({visible: isVisible}); + await this.isVisible(this.property.filter({hasText: blockName}).locator(this.addBlockElementBtn).filter({hasText: label}), isVisible); } async doesFormValidationMessageContainText(text: string) { - await expect(this.formValidationMessage).toContainText(text); + await this.containsText(this.formValidationMessage, text); } async doesBlockElementHaveName(name: string) { - await expect(this.blockName).toContainText(name); + await this.containsText(this.blockName, name); } async clickAddBlockSettingsTabButton() { @@ -1235,7 +1235,7 @@ export class ContentUiHelper extends UiBaseLocators { const blockWithArea = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: blockWithAreaName})).nth(index); const area = blockWithArea.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); const blockInArea = area.locator(this.blockGridEntry.filter({hasText: blockInAreaName})); - await expect(blockInArea).toBeVisible(); + await this.waitForVisible(blockInArea); } async doesBlockContainBlockCountInArea(blockWithAreaName: string, areaName: string, blocksInAreaCount: number, index: number = 0) { @@ -1459,23 +1459,23 @@ export class ContentUiHelper extends UiBaseLocators { } async doesPublishAtValidationMessageContainText(text: string) { - await expect(this.publishAtValidationMessage).toContainText(text); + await this.containsText(this.publishAtValidationMessage, text); } async doesUnpublishAtValidationMessageContainText(text: string) { - await expect(this.unpublishAtValidationMessage).toContainText(text); + await this.containsText(this.unpublishAtValidationMessage, text); } async doesLastPublishedContainText(text: string) { - await expect(this.lastPublished).toContainText(text); + await this.containsText(this.lastPublished, text); } async doesPublishAtContainText(text: string) { - await expect(this.publishAt).toContainText(text); + await this.containsText(this.publishAt, text); } async doesRemoveAtContainText(text: string) { - await expect(this.removeAt).toContainText(text); + await this.containsText(this.removeAt, text); } async clickSelectAllCheckbox() { @@ -1495,11 +1495,11 @@ export class ContentUiHelper extends UiBaseLocators { } async doesTiptapHaveWordCount(count: number) { - await expect(this.tiptapStatusbarWordCount).toHaveText(`${count} words`); + await this.hasText(this.tiptapStatusbarWordCount, `${count} words`); } async doesTiptapHaveCharacterCount(count: number) { - await expect(this.tiptapStatusbarWordCount).toHaveText(`${count} characters`); + await this.hasText(this.tiptapStatusbarWordCount, `${count} characters`); } async clickTiptapWordCountButton() { @@ -1507,7 +1507,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesElementPathHaveText(text: string) { - await expect(this.tiptapStatusbarElementPath).toHaveText(text); + await this.hasText(this.tiptapStatusbarElementPath, text); } async clickConfirmToPublishButton() { @@ -1531,7 +1531,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesDocumentVariantLanguageItemHaveName(name: string) { - await expect(this.documentVariantLanguagePicker).toContainText(name); + await this.containsText(this.documentVariantLanguagePicker, name); } async clickSchedulePublishLanguageButton(languageName: string) { @@ -1584,7 +1584,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesModalFormValidationMessageContainText(text: string) { - await expect(this.modalFormValidationMessage).toContainText(text); + await this.containsText(this.modalFormValidationMessage, text); } async enterSearchKeywordInTreePickerModal(keyword: string) { @@ -1618,13 +1618,13 @@ export class ContentUiHelper extends UiBaseLocators { } else { expect(count, `Expected only one element, but found ${count}`).toBe(0); } - await expect(this.refListBlock).toBeVisible({visible: isVisible}); + await this.isVisible(this.refListBlock, isVisible); } async doesBlockCustomViewHaveValue(customBlockViewLocator: string, valueText: string) { const locator = this.page.locator(`${customBlockViewLocator} p`); - await expect(locator).toBeVisible(); - await expect(locator).toHaveText(valueText); + await this.waitForVisible(locator); + await this.hasText(locator, valueText); } async clickPropertyActionWithName(name: string) { @@ -1633,7 +1633,7 @@ export class ContentUiHelper extends UiBaseLocators { } async isContentWithNameVisibleInList(contentName: string, isVisible: boolean = true) { - await expect(this.documentTableColumnName.filter({hasText: contentName})).toBeVisible({visible: isVisible}); + await this.isVisible(this.documentTableColumnName.filter({hasText: contentName}), isVisible); } async selectDocumentBlueprintWithName(blueprintName: string) { @@ -1641,7 +1641,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesDocumentModalHaveText(text: string) { - await expect(this.documentCreateOptionsModal).toContainText(text); + await this.containsText(this.documentCreateOptionsModal, text); } async doesListViewItemsHaveCount(pageSize: number){ @@ -1673,7 +1673,7 @@ export class ContentUiHelper extends UiBaseLocators { } async isChooseButtonVisible(isVisible: boolean = true) { - await expect(this.chooseBtn).toBeVisible({visible: isVisible}); + await this.isVisible(this.chooseBtn, isVisible); } async clickDocumentNotificationOptionWithName(name: string) { @@ -1700,7 +1700,7 @@ export class ContentUiHelper extends UiBaseLocators { async isAddBlockListElementWithNameVisible(blockName: string) { const createNewButtonLocator = this.page.getByTestId(`property:${blockName.toLowerCase()}`).locator('uui-button[label="Create new"]'); - await expect(createNewButtonLocator).toBeVisible(); + await this.waitForVisible(createNewButtonLocator); await expect(createNewButtonLocator).not.toHaveAttribute('disabled'); } @@ -1711,7 +1711,7 @@ export class ContentUiHelper extends UiBaseLocators { async isBlockPropertyEditable(propertyName: string, isEditable: boolean = true) { const propertyLocator = this.blockProperty.filter({hasText: propertyName}).locator('#input'); - await expect(propertyLocator).toBeVisible(); + await this.waitForVisible(propertyLocator); await expect(propertyLocator).toBeEditable({editable: isEditable}); } } \ No newline at end of file diff --git a/lib/helpers/DataTypeUiHelper.ts b/lib/helpers/DataTypeUiHelper.ts index 2be1870c..6b609ae8 100644 --- a/lib/helpers/DataTypeUiHelper.ts +++ b/lib/helpers/DataTypeUiHelper.ts @@ -1117,7 +1117,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async doesSettingsContainText(text: string) { - await expect(this.propertyEditorConfig).toContainText(text); + await this.containsText(this.propertyEditorConfig, text); } async clickStatusbarItemInToolboxWithName(name: string) { diff --git a/lib/helpers/DictionaryUiHelper.ts b/lib/helpers/DictionaryUiHelper.ts index ea866ff5..6e8cf67b 100644 --- a/lib/helpers/DictionaryUiHelper.ts +++ b/lib/helpers/DictionaryUiHelper.ts @@ -107,7 +107,7 @@ export class DictionaryUiHelper extends UiBaseLocators { } async isSearchResultMessageDisplayEmpty(message: string) { - return await expect(this.emptySearchResultMessage).toHaveText(message); + await this.hasText(this.emptySearchResultMessage, message); } async isDictionaryTreeItemVisible(dictionaryName: string, isVisible: boolean = true) { @@ -115,6 +115,6 @@ export class DictionaryUiHelper extends UiBaseLocators { } async doesDictionaryCollectionContainText(text: string) { - return await expect(this.dictionaryCollection).toContainText(text); + await this.containsText(this.dictionaryCollection, text); } } \ No newline at end of file diff --git a/lib/helpers/ExamineManagementUiHelper.ts b/lib/helpers/ExamineManagementUiHelper.ts index a2100162..876c8c1f 100644 --- a/lib/helpers/ExamineManagementUiHelper.ts +++ b/lib/helpers/ExamineManagementUiHelper.ts @@ -20,7 +20,7 @@ export class ExamineManagementUiHelper extends UiBaseLocators { } async doesIndexersHaveText(text: string) { - return await expect(this.indexersContent).toContainText(text); + await this.containsText(this.indexersContent, text); } checkIndexersCount() { diff --git a/lib/helpers/FormsUiHelper.ts b/lib/helpers/FormsUiHelper.ts index 82e282c4..a7009a16 100644 --- a/lib/helpers/FormsUiHelper.ts +++ b/lib/helpers/FormsUiHelper.ts @@ -1,5 +1,5 @@ import { UiBaseLocators } from "./UiBaseLocators"; -import { expect, Locator, Page } from "@playwright/test"; +import { Locator, Page } from "@playwright/test"; import { ConstantHelper } from "./ConstantHelper"; export class FormsUiHelper extends UiBaseLocators { @@ -188,7 +188,7 @@ export class FormsUiHelper extends UiBaseLocators { } async doesFormTreeHaveFormName(name: string) { - await expect(this.formTree).toContainText(name); + await this.containsText(this.formTree, name); } async goToFormWithName(name: string) { diff --git a/lib/helpers/LogViewerUiHelper.ts b/lib/helpers/LogViewerUiHelper.ts index 5162d3b8..7df9ef98 100644 --- a/lib/helpers/LogViewerUiHelper.ts +++ b/lib/helpers/LogViewerUiHelper.ts @@ -78,7 +78,7 @@ export class LogViewerUiHelper extends UiBaseLocators { } async doesFirstLogHaveTimestamp(timestamp: string) { - return await expect(this.firstLogLevelTimestamp).toContainText(timestamp); + await this.containsText(this.firstLogLevelTimestamp, timestamp); } async clickPageNumber(pageNumber: number) { @@ -86,7 +86,7 @@ export class LogViewerUiHelper extends UiBaseLocators { } async doesFirstLogHaveMessage(message: string) { - await expect(this.firstLogLevelMessage).toContainText(message, {timeout: 10000}); + await this.containsText(this.firstLogLevelMessage, message, 10000); } async clickSavedSearchByName(name: string) { diff --git a/lib/helpers/MediaUiHelper.ts b/lib/helpers/MediaUiHelper.ts index 396568a0..b14b3caa 100644 --- a/lib/helpers/MediaUiHelper.ts +++ b/lib/helpers/MediaUiHelper.ts @@ -71,7 +71,7 @@ export class MediaUiHelper extends UiBaseLocators { } async doesMediaCardContainText(name: string) { - await expect(this.mediaCardItems).toContainText(name); + await this.containsText(this.mediaCardItems, name); } async clickTrashButton() { @@ -165,15 +165,15 @@ export class MediaUiHelper extends UiBaseLocators { } async isMediaGridViewVisible(isVisible: boolean = true) { - return expect(this.mediaGridView).toBeVisible({visible: isVisible}); + await this.isVisible(this.mediaGridView, isVisible); } async isMediaListViewVisible(isVisible: boolean = true) { - return expect(this.mediaListView).toBeVisible({visible: isVisible}); + await this.isVisible(this.mediaListView, isVisible); } async doesMediaWorkspaceHaveText(text: string) { - return expect(this.mediaWorkspace).toContainText(text); + await this.containsText(this.mediaWorkspace, text); } async clickBulkTrashButton() { diff --git a/lib/helpers/ModelsBuilderUiHelper.ts b/lib/helpers/ModelsBuilderUiHelper.ts index 70cad57d..a762288b 100644 --- a/lib/helpers/ModelsBuilderUiHelper.ts +++ b/lib/helpers/ModelsBuilderUiHelper.ts @@ -1,4 +1,4 @@ -import {Page, Locator, expect} from "@playwright/test"; +import {Page, Locator} from "@playwright/test"; import {UiBaseLocators} from "./UiBaseLocators"; export class ModelsBuilderUiHelper extends UiBaseLocators { @@ -16,6 +16,6 @@ export class ModelsBuilderUiHelper extends UiBaseLocators { } async doesModelsBuilderDashboardHaveText(text: string) { - return await expect(this.modelsBuilderDashboardContent).toContainText(text, {timeout: 10000}); + await this.containsText(this.modelsBuilderDashboardContent, text, 10000); } } diff --git a/lib/helpers/PackageUiHelper.ts b/lib/helpers/PackageUiHelper.ts index 31c87509..b126a93c 100644 --- a/lib/helpers/PackageUiHelper.ts +++ b/lib/helpers/PackageUiHelper.ts @@ -57,7 +57,7 @@ export class PackageUiHelper extends UiBaseLocators { } async isUmbracoBackofficePackageVisible(isVisible = true) { - return await expect(this.umbracoBackofficePackage).toBeVisible({visible: isVisible}); + await this.isVisible(this.umbracoBackofficePackage, isVisible); } async clickCreatedTab() { @@ -79,7 +79,7 @@ export class PackageUiHelper extends UiBaseLocators { } async isMarketPlaceIFrameVisible(isVisible = true) { - return await expect(this.marketPlaceIFrame).toBeVisible({visible: isVisible}); + await this.isVisible(this.marketPlaceIFrame, isVisible); } async clickCreatePackageButton() { diff --git a/lib/helpers/PartialViewUiHelper.ts b/lib/helpers/PartialViewUiHelper.ts index 87496801..f6c55de7 100644 --- a/lib/helpers/PartialViewUiHelper.ts +++ b/lib/helpers/PartialViewUiHelper.ts @@ -54,7 +54,7 @@ export class PartialViewUiHelper extends UiBaseLocators { async enterPartialViewName(partialViewName: string) { await this.enterText(this.enterAName, partialViewName); - await expect(this.enterAName).toHaveValue(partialViewName); + await this.hasValue(this.enterAName, partialViewName); } async enterPartialViewContent(partialViewContent: string) { @@ -83,7 +83,7 @@ export class PartialViewUiHelper extends UiBaseLocators { } async waitUntilPartialViewLoaderIsNoLongerVisible() { - await expect(this.partialViewUiLoader).toBeVisible({visible: false}); + await this.isVisible(this.partialViewUiLoader, false); } async isPartialViewRootTreeItemVisible(partialView: string, isVisible: boolean = true, toReload: boolean = true) { diff --git a/lib/helpers/RelationTypeUiHelper.ts b/lib/helpers/RelationTypeUiHelper.ts index df65c797..0974d2ba 100644 --- a/lib/helpers/RelationTypeUiHelper.ts +++ b/lib/helpers/RelationTypeUiHelper.ts @@ -64,19 +64,19 @@ export class RelationTypeUiHelper extends UiBaseLocators{ } async doesParentTypeContainValue(value: string) { - await expect(this.relationTypeParentType).toContainText(value); + await this.containsText(this.relationTypeParentType, value); } async doesChildTypeContainValue(value: string) { - await expect(this.relationTypeChildType).toContainText(value); + await this.containsText(this.relationTypeChildType, value); } async doesBidirectionalContainValue(value: string) { - await expect(this.relationTypeBidirectional).toContainText(value); + await this.containsText(this.relationTypeBidirectional, value); } async doesDependencyContainValue(value: string) { - await expect(this.relationTypeDependency).toContainText(value); + await this.containsText(this.relationTypeDependency, value); } async isRelationWithParentAndChildVisible(parent: string, child: string, isVisible: boolean = true) { diff --git a/lib/helpers/TelemetryDataUiHelper.ts b/lib/helpers/TelemetryDataUiHelper.ts index 127bcf62..1f7a7c8d 100644 --- a/lib/helpers/TelemetryDataUiHelper.ts +++ b/lib/helpers/TelemetryDataUiHelper.ts @@ -1,4 +1,4 @@ -import {Page, Locator, expect} from "@playwright/test"; +import {Page, Locator} from "@playwright/test"; import {UiBaseLocators} from "./UiBaseLocators"; export class TelemetryDataUiHelper extends UiBaseLocators { @@ -20,6 +20,6 @@ export class TelemetryDataUiHelper extends UiBaseLocators { } async doesTelemetryDataLevelHaveValue(value: string) { - return await expect(this.telemetryDataLevelToggle).toHaveValue(value); + await this.hasValue(this.telemetryDataLevelToggle, value); } } diff --git a/lib/helpers/TemplateUiHelper.ts b/lib/helpers/TemplateUiHelper.ts index 0f1da7de..cc936a6d 100644 --- a/lib/helpers/TemplateUiHelper.ts +++ b/lib/helpers/TemplateUiHelper.ts @@ -47,11 +47,11 @@ export class TemplateUiHelper extends UiBaseLocators { await this.reloadTemplateTree(); if (childTemplateName === '') { await this.click(this.page.getByLabel(templateName, {exact: true})); - await expect(this.enterAName).toHaveValue(templateName); + await this.hasValue(this.enterAName, templateName); } else { await this.openCaretButtonForName(templateName); await this.click(this.page.getByLabel(childTemplateName, {exact: true})); - await expect(this.enterAName).toHaveValue(childTemplateName); + await this.hasValue(this.enterAName, childTemplateName); } await this.page.waitForTimeout(ConstantHelper.wait.medium); } diff --git a/lib/helpers/UserGroupUiHelper.ts b/lib/helpers/UserGroupUiHelper.ts index d0d85270..1468940b 100644 --- a/lib/helpers/UserGroupUiHelper.ts +++ b/lib/helpers/UserGroupUiHelper.ts @@ -191,7 +191,7 @@ export class UserGroupUiHelper extends UiBaseLocators { } async doesUserGroupContainSection(section: string) { - return await expect(this.sectionList).toContainText(section); + await this.containsText(this.sectionList, section); } async doesUserGroupHaveSections(sections: string[]) { diff --git a/lib/helpers/UserUiHelper.ts b/lib/helpers/UserUiHelper.ts index 5757db51..78fd1ba2 100644 --- a/lib/helpers/UserUiHelper.ts +++ b/lib/helpers/UserUiHelper.ts @@ -197,7 +197,7 @@ export class UserUiHelper extends UiBaseLocators { } async doesUserSectionContainUserWithText(name: string) { - return await expect(this.userGrid).toContainText(name); + await this.containsText(this.userGrid, name); } async filterByStatusName(statusName: string) { @@ -243,11 +243,11 @@ export class UserUiHelper extends UiBaseLocators { } async isUserDisabledTextVisible() { - return await expect(this.disabledTxt).toBeVisible(); + await this.waitForVisible(this.disabledTxt); } async isUserActiveTextVisible() { - return await expect(this.activeTxt).toBeVisible(); + await this.waitForVisible(this.activeTxt); } async orderByNewestUser() { @@ -289,7 +289,7 @@ export class UserUiHelper extends UiBaseLocators { } async isGoToProfileButtonVisible(isVisible: boolean = true) { - await expect(this.goToProfileBtn).toBeVisible({visible: isVisible}); + await this.isVisible(this.goToProfileBtn, isVisible); } async clickAPIUserButton() { diff --git a/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts b/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts index bd191438..649fdada 100644 --- a/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts +++ b/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts @@ -1,4 +1,4 @@ -import {Page, Locator, expect} from "@playwright/test"; +import {Page, Locator} from "@playwright/test"; import {UiBaseLocators} from "../UiBaseLocators"; export class InstallUiHelper extends UiBaseLocators { @@ -40,7 +40,7 @@ export class InstallUiHelper extends UiBaseLocators { } async doesDatabaseHaveType(databaseType: string) { - await expect(this.databaseType).toHaveText(databaseType); + await this.hasText(this.databaseType, databaseType); } async clickInstallButton() { From 6fb6809e66e1ce0e7ef68932b222e0a6135b9f11 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 17:35:46 +0700 Subject: [PATCH 17/34] Refactor UI helpers to use hasCount assertion method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace toHaveCount assertions with hasCount() wrapper method from BasePage and remove duplicate waitForCount method. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- lib/helpers/BasePage.ts | 10 -------- lib/helpers/ContentUiHelper.ts | 30 +++++++++++------------ lib/helpers/DataTypeUiHelper.ts | 10 ++++---- lib/helpers/LogViewerUiHelper.ts | 4 +-- lib/helpers/MediaUiHelper.ts | 4 +-- lib/helpers/RedirectManagementUiHelper.ts | 4 +-- lib/helpers/UiBaseLocators.ts | 2 +- lib/helpers/UserGroupUiHelper.ts | 2 +- 8 files changed, 28 insertions(+), 38 deletions(-) diff --git a/lib/helpers/BasePage.ts b/lib/helpers/BasePage.ts index a4f8569b..5c50c597 100644 --- a/lib/helpers/BasePage.ts +++ b/lib/helpers/BasePage.ts @@ -347,16 +347,6 @@ export class BasePage { await expect(locator).toHaveClass(className, { timeout: timeout ?? 5000 }); } - /** - * Waits for a specific number of elements to exist. - * @param locator - The locator to count - * @param count - The expected count - * @param timeout - Maximum time to wait in milliseconds - */ - async waitForCount(locator: Locator, count: number, timeout?: number): Promise { - await expect(locator).toHaveCount(count, { timeout: timeout ?? 5000 }); - } - /** * Waits for an element to be editable. * @param locator - The element to wait for diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index 9dc9277f..9f67e464 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -898,7 +898,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesListViewContainCount(count: number) { - await expect(this.listViewTableRow).toHaveCount(count); + await this.hasCount(this.listViewTableRow, count); } async selectContentWithNameInListView(name: string) { @@ -1002,7 +1002,7 @@ export class ContentUiHelper extends UiBaseLocators { } async isDocumentTreeEmpty() { - await expect(this.documentTreeItem).toHaveCount(0); + await this.hasCount(this.documentTreeItem, 0); } async doesDocumentWorkspaceContainName(name: string) { @@ -1242,14 +1242,14 @@ export class ContentUiHelper extends UiBaseLocators { const blockWithArea = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: blockWithAreaName})).nth(index); const area = blockWithArea.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); const blocks = area.locator(this.blockGridEntry); - await expect(blocks).toHaveCount(blocksInAreaCount); + await this.hasCount(blocks, blocksInAreaCount); } async doesBlockContainCountOfBlockInArea(blockWithAreaName: string, areaName: string, blockInAreaName: string, count: number, index: number = 0) { const blockWithArea = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: blockWithAreaName})).nth(index); const area = blockWithArea.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); const blockInArea = area.locator(this.blockGridEntry.filter({hasText: blockInAreaName})); - await expect(blockInArea).toHaveCount(count); + await this.hasCount(blockInArea, count); } async getBlockAtRootDataElementKey(blockName: string, index: number = 0) { @@ -1279,13 +1279,13 @@ export class ContentUiHelper extends UiBaseLocators { async doesBlockAreaContainColumnSpan(blockWithAreaName: string, areaName: string, columnSpan: number, index: number = 0) { const blockWithArea = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: blockWithAreaName})).nth(index); const area = blockWithArea.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); - await expect(area).toHaveAttribute('data-area-col-span', columnSpan.toString()); + await this.hasAttribute(area, 'data-area-col-span', columnSpan.toString()); } async doesBlockAreaContainRowSpan(blockWithAreaName: string, areaName: string, rowSpan: number, index: number = 0) { const blockWithArea = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: blockWithAreaName})).nth(index); const area = blockWithArea.locator(this.blockGridAreasContainer).locator(`[data-area-alias="${areaName}"]`); - await expect(area).toHaveAttribute('data-area-row-span', rowSpan.toString()); + await this.hasAttribute(area, 'data-area-row-span', rowSpan.toString()); } async clickInlineAddToAreaButton(parentBlockName: string, areaName: string, parentIndex: number = 0, buttonIndex: number = 1) { @@ -1337,7 +1337,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesClipboardContainCopiedBlocksCount(count: number) { - await expect(this.clipboardEntryPicker.locator(this.menuItem)).toHaveCount(count); + await this.hasCount(this.clipboardEntryPicker.locator(this.menuItem), count); } async selectClipboardEntryWithName(contentName: string, propertyName: string, blockName: string, index: number = 0) { @@ -1373,11 +1373,11 @@ export class ContentUiHelper extends UiBaseLocators { } async doesBlockListPropertyHaveBlockAmount(groupName: string, propertyName: string, amount: number) { - await expect(this.workspaceEditTab.filter({hasText: groupName}).locator(this.workspaceEditProperties).filter({hasText: propertyName}).locator(this.blockListEntry)).toHaveCount(amount); + await this.hasCount(this.workspaceEditTab.filter({hasText: groupName}).locator(this.workspaceEditProperties).filter({hasText: propertyName}).locator(this.blockListEntry), amount); } async doesBlockGridPropertyHaveBlockAmount(groupName: string, propertyName: string, amount: number) { - await expect(this.workspaceEditTab.filter({hasText: groupName}).locator(this.workspaceEditProperties).filter({hasText: propertyName}).locator(this.blockGridEntry)).toHaveCount(amount); + await this.hasCount(this.workspaceEditTab.filter({hasText: groupName}).locator(this.workspaceEditProperties).filter({hasText: propertyName}).locator(this.blockGridEntry), amount); } async doesPropertyContainValidationMessage(groupName: string, propertyName: string, message: string) { @@ -1403,8 +1403,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesUploadedSvgThumbnailHaveSrc(imageSrc: string) { - await this.waitForVisible(this.uploadedSvgThumbnail); - await expect(this.uploadedSvgThumbnail).toHaveAttribute('src', imageSrc); + await this.hasAttribute(this.uploadedSvgThumbnail, 'src', imageSrc); } async doesRichTextEditorBlockContainLabel(richTextEditorAlias: string, label: string) { @@ -1484,9 +1483,10 @@ export class ContentUiHelper extends UiBaseLocators { async doesSchedulePublishModalButtonContainDisabledTag(hasDisabledTag: boolean = false) { if (!hasDisabledTag) { - return await expect(this.schedulePublishModalBtn).not.toHaveAttribute('disabled', ''); + await expect(this.schedulePublishModalBtn).not.toHaveAttribute('disabled', ''); + } else { + await this.hasAttribute(this.schedulePublishModalBtn, 'disabled', ''); } - return await expect(this.schedulePublishModalBtn).toHaveAttribute('disabled', ''); } async clickInlineBlockCaretButtonForName(blockEditorName: string, index: number = 0) { @@ -1527,7 +1527,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesDocumentVariantLanguageItemHaveCount(count: number) { - await expect(this.documentVariantLanguageItem).toHaveCount(count); + await this.hasCount(this.documentVariantLanguageItem, count); } async doesDocumentVariantLanguageItemHaveName(name: string) { @@ -1645,7 +1645,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesListViewItemsHaveCount(pageSize: number){ - await expect(this.listViewCustomRows).toHaveCount(pageSize); + await this.hasCount(this.listViewCustomRows, pageSize); } async isListViewItemWithNameVisible(itemName: string, index: number = 0){ diff --git a/lib/helpers/DataTypeUiHelper.ts b/lib/helpers/DataTypeUiHelper.ts index 6b609ae8..ccecdfaf 100644 --- a/lib/helpers/DataTypeUiHelper.ts +++ b/lib/helpers/DataTypeUiHelper.ts @@ -1084,15 +1084,15 @@ export class DataTypeUiHelper extends UiBaseLocators { } async doesPropertyEditorHaveUiAlias(uiAlias: string) { - await expect(this.propertyEditor).toHaveAttribute('alias', uiAlias); + await this.hasAttribute(this.propertyEditor, 'alias', uiAlias); } async doesPropertyEditorHaveName(name: string) { - await expect(this.propertyEditor).toHaveAttribute('name', name); + await this.hasAttribute(this.propertyEditor, 'name', name); } async doesPropertyEditorHaveAlias(alias: string) { - await expect(this.propertyEditor).toHaveAttribute('property-editor-schema-alias', alias); + await this.hasAttribute(this.propertyEditor, 'property-editor-schema-alias', alias); } async clickDataTypeButton() { @@ -1113,7 +1113,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async doesSettingItemsHaveCount(settings) { - await expect(this.propertyEditorConfigItems).toHaveCount(Object.keys(settings).length); + await this.hasCount(this.propertyEditorConfigItems, Object.keys(settings).length); } async doesSettingsContainText(text: string) { @@ -1135,7 +1135,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async doesBlockHaveThumbnailImage(thumbnailImageUrl: string) { - await expect(this.blockThumbnailImage).toHaveAttribute('src', thumbnailImageUrl); + await this.hasAttribute(this.blockThumbnailImage, 'src', thumbnailImageUrl); } async addTimeZones(timeZones: string[]) { diff --git a/lib/helpers/LogViewerUiHelper.ts b/lib/helpers/LogViewerUiHelper.ts index 7df9ef98..d0fedceb 100644 --- a/lib/helpers/LogViewerUiHelper.ts +++ b/lib/helpers/LogViewerUiHelper.ts @@ -59,7 +59,7 @@ export class LogViewerUiHelper extends UiBaseLocators { } async doesLogLevelCountMatch(level: string, expectedNumber: number) { - return await expect(this.page.locator('umb-log-viewer-message').locator('umb-log-viewer-level-tag', {hasText: level})).toHaveCount(expectedNumber); + await this.hasCount(this.page.locator('umb-log-viewer-message').locator('umb-log-viewer-level-tag', {hasText: level}), expectedNumber); } async saveSearch(searchName: string) { @@ -117,6 +117,6 @@ export class LogViewerUiHelper extends UiBaseLocators { } async waitUntilLoadingSpinnerInvisible() { - await expect(this.loadingSpinner).toHaveCount(0); + await this.hasCount(this.loadingSpinner, 0); } } diff --git a/lib/helpers/MediaUiHelper.ts b/lib/helpers/MediaUiHelper.ts index b14b3caa..8741eded 100644 --- a/lib/helpers/MediaUiHelper.ts +++ b/lib/helpers/MediaUiHelper.ts @@ -67,7 +67,7 @@ export class MediaUiHelper extends UiBaseLocators { } async doesMediaCardsContainAmount(count: number) { - await expect(this.mediaCardItems).toHaveCount(count); + await this.hasCount(this.mediaCardItems, count); } async doesMediaCardContainText(name: string) { @@ -200,7 +200,7 @@ export class MediaUiHelper extends UiBaseLocators { async doesMediaItemInTreeHaveThumbnail(name: string, thumbnailIconName: string) { const mediaThumbnailIconLocator = this.page.locator(`umb-media-tree-item [label="${name}"]`).locator('#icon-container #icon'); - await expect(mediaThumbnailIconLocator).toHaveAttribute('name', thumbnailIconName); + await this.hasAttribute(mediaThumbnailIconLocator, 'name', thumbnailIconName); } async isChildMediaVisible(parentName: string, childName: string, isVisible: boolean = true) { diff --git a/lib/helpers/RedirectManagementUiHelper.ts b/lib/helpers/RedirectManagementUiHelper.ts index bbc27809..f2ca5d8d 100644 --- a/lib/helpers/RedirectManagementUiHelper.ts +++ b/lib/helpers/RedirectManagementUiHelper.ts @@ -1,4 +1,4 @@ -import {Page, Locator, expect} from "@playwright/test"; +import {Page, Locator} from "@playwright/test"; import {UiBaseLocators} from "./UiBaseLocators"; export class RedirectManagementUiHelper extends UiBaseLocators { @@ -47,6 +47,6 @@ export class RedirectManagementUiHelper extends UiBaseLocators { } async doesRedirectManagementRowsHaveCount(itemCount: number) { - await expect(this.redirectManagementRows).toHaveCount(itemCount); + await this.hasCount(this.redirectManagementRows, itemCount); } } diff --git a/lib/helpers/UiBaseLocators.ts b/lib/helpers/UiBaseLocators.ts index 3a89a1d1..b1a2a45d 100644 --- a/lib/helpers/UiBaseLocators.ts +++ b/lib/helpers/UiBaseLocators.ts @@ -733,7 +733,7 @@ export class UiBaseLocators extends BasePage { } async doesSuccessNotificationsHaveCount(count: number) { - return await expect(this.successNotification).toHaveCount(count); + await this.hasCount(this.successNotification, count); } async doesSuccessNotificationHaveText(text: string, isVisible: boolean = true, deleteNotification = false, timeout = 5000) { diff --git a/lib/helpers/UserGroupUiHelper.ts b/lib/helpers/UserGroupUiHelper.ts index 1468940b..977e9d88 100644 --- a/lib/helpers/UserGroupUiHelper.ts +++ b/lib/helpers/UserGroupUiHelper.ts @@ -201,6 +201,6 @@ export class UserGroupUiHelper extends UiBaseLocators { } async doesUserGroupSectionsHaveCount(count: number) { - return await expect(this.section).toHaveCount(count); + await this.hasCount(this.section, count); } } \ No newline at end of file From 2f6838174b478930d69b48bf598f68d2881444be Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 17:37:41 +0700 Subject: [PATCH 18/34] Bumped version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4562eb20..a08cf490 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "17.0.18", + "version": "17.1.0-beta", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@umbraco/playwright-testhelpers", - "version": "17.0.18", + "version": "17.1.0-beta", "license": "MIT", "dependencies": { "@umbraco/json-models-builders": "2.0.42", diff --git a/package.json b/package.json index a46871f4..5ea0e66e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "17.0.18", + "version": "17.1.0-beta", "description": "Test helpers for making playwright tests for Umbraco solutions", "main": "dist/lib/index.js", "files": [ From 18f6565a2d8e6c11f98d55f6f04351ac4ea492a5 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 23:26:48 +0700 Subject: [PATCH 19/34] Reverted code --- lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts b/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts index 649fdada..500b7192 100644 --- a/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts +++ b/lib/helpers/differentAppSettingsHelpers/InstallUiHelper.ts @@ -36,7 +36,7 @@ export class InstallUiHelper extends UiBaseLocators { } async setDatabaseType(databaseType: string) { - await this.selectByValue(this.databaseTypeInput, databaseType); + await this.databaseTypeInput.selectOption(databaseType) } async doesDatabaseHaveType(databaseType: string) { From 9b13c7f0cb2d581a8bb730e98e9967c026587ae9 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 23:27:05 +0700 Subject: [PATCH 20/34] Added more timeout --- lib/helpers/DataTypeUiHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helpers/DataTypeUiHelper.ts b/lib/helpers/DataTypeUiHelper.ts index ccecdfaf..6b9aca9b 100644 --- a/lib/helpers/DataTypeUiHelper.ts +++ b/lib/helpers/DataTypeUiHelper.ts @@ -844,7 +844,7 @@ export class DataTypeUiHelper extends UiBaseLocators { } async addBlockSettingsModel(elementName: string) { - await this.click(this.chooseSettingsModelBtn); + await this.click(this.chooseSettingsModelBtn, {timeout: ConstantHelper.timeout.long}); await this.clickModalMenuItemWithName(elementName); await this.clickChooseModalButton(); } From 566dcc086eee3751bced172cc08fb6936f42e125 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Thu, 11 Dec 2025 23:51:37 +0700 Subject: [PATCH 21/34] Increase wait for backoffice main to be visible --- lib/helpers/UiBaseLocators.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helpers/UiBaseLocators.ts b/lib/helpers/UiBaseLocators.ts index b1a2a45d..f48e3f32 100644 --- a/lib/helpers/UiBaseLocators.ts +++ b/lib/helpers/UiBaseLocators.ts @@ -811,7 +811,7 @@ export class UiBaseLocators extends BasePage { } async isBackOfficeMainVisible(isVisible: boolean = true) { - await this.page.waitForTimeout(ConstantHelper.wait.medium); + await this.page.waitForTimeout(ConstantHelper.timeout.medium); await this.isVisible(this.backOfficeMain, isVisible); } From dc70f8d79f927259ae5a869876ae5547efd6904c Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 12 Dec 2025 00:17:35 +0700 Subject: [PATCH 22/34] Use isVisible() instead of toBeVisible() --- lib/helpers/ContentUiHelper.ts | 56 +++++++++++++++++----------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index 9f67e464..7b93765a 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -240,7 +240,7 @@ export class ContentUiHelper extends UiBaseLocators { // Culture and Hostname this.cultureAndHostnamesBtn = page.getByLabel(/^Culture and Hostnames(…)?$/); this.hostNameItem = page.locator('.hostname-item'); - this.cultureLanguageDropdownBox = this.hostNameItem.locator('[label="Culture"]').getByLabel('combobox-input'); + this.cultureLanguageDropdownBox = this.page.locator('[label="Culture"]').getByLabel('combobox-input'); this.hostnameTxt = page.getByLabel('Hostname', {exact: true}); this.hostnameLanguageDropdownBox = this.hostNameItem.locator('[label="Culture"]').getByLabel('combobox-input'); this.deleteHostnameBtn = this.hostNameItem.locator('[name="icon-trash"] svg'); @@ -380,7 +380,7 @@ export class ContentUiHelper extends UiBaseLocators { async isSuccessStateVisibleForSaveAndPublishButton (isVisible: boolean = true){ const saveAndPublishBtn = this.workspaceAction.filter({has: this.saveAndPublishBtn}); - await expect(saveAndPublishBtn.locator(this.successState)).toBeVisible({visible: isVisible, timeout: ConstantHelper.timeout.long}); + await this.isVisible(saveAndPublishBtn.locator(this.successState), isVisible, ConstantHelper.timeout.long); } async clickPublishButton() { @@ -531,11 +531,11 @@ export class ContentUiHelper extends UiBaseLocators { } async isDocumentTypeModalVisible(documentTypeName: string) { - await expect(this.documentTypeWorkspace.filter({hasText: documentTypeName})).toBeVisible(); + await this.isVisible(this.documentTypeWorkspace.filter({hasText: documentTypeName})); } async isTemplateModalVisible(templateName: string) { - await expect(this.breadcrumbsTemplateModal.getByText(templateName)).toBeVisible(); + await this.isVisible(this.breadcrumbsTemplateModal.getByText(templateName)); } async clickEditTemplateByName(templateName: string) { @@ -549,8 +549,8 @@ export class ContentUiHelper extends UiBaseLocators { } async isTemplateNameDisabled(templateName: string) { - await expect(this.sidebarModal.getByLabel(templateName)).toBeVisible(); - await expect(this.sidebarModal.getByLabel(templateName)).toBeDisabled(); + await this.isVisible(this.sidebarModal.getByLabel(templateName)); + await this.isDisabled(this.sidebarModal.getByLabel(templateName)); } // Culture and Hostnames @@ -564,7 +564,7 @@ export class ContentUiHelper extends UiBaseLocators { async selectCultureLanguageOption(option: string) { await this.click(this.cultureLanguageDropdownBox); - await this.click(this.hostNameItem.getByText(option, {exact: true})); + await this.click(this.page.getByText(option, {exact: true})); } async selectHostnameLanguageOption(option: string, index: number = 0) { @@ -607,7 +607,7 @@ export class ContentUiHelper extends UiBaseLocators { } async isOpenButtonVisibleInContentPicker(contentPickerName: string, isVisible: boolean = true) { - return expect(this.page.getByLabel('Open ' + contentPickerName)).toBeVisible({visible: isVisible}); + return await this.isVisible(this.page.getByLabel('Open ' + contentPickerName), isVisible); } async clickContentPickerOpenButton(contentPickerName: string) { @@ -615,19 +615,19 @@ export class ContentUiHelper extends UiBaseLocators { } async isNodeOpenForContentPicker(contentPickerName: string) { - return expect(this.openedModal.getByText(contentPickerName)).toBeVisible(); + return await this.isVisible(this.openedModal.getByText(contentPickerName)); } async isContentNameVisible(contentName: string, isVisible: boolean = true) { - return expect(this.sidebarModal.getByText(contentName)).toBeVisible({visible: isVisible}); + return await this.isVisible(this.sidebarModal.getByText(contentName), isVisible); } async isContentInTreeVisible(name: string, isVisible: boolean = true) { - await expect(this.documentTreeItem.getByLabel(name, {exact: true}).first()).toBeVisible({visible: isVisible}); + await this.isVisible(this.documentTreeItem.locator('[label="' + name + '"]'), isVisible); } async isChildContentInTreeVisible(parentName: string, childName: string, isVisible: boolean = true) { - await expect(this.documentTreeItem.locator('[label="' + parentName + '"]').getByLabel(childName)).toBeVisible({visible: isVisible}); + await this.isVisible(this.documentTreeItem.locator('[label="' + parentName + '"]').locator('[uui-menu-item[label="' + childName + '"]'), isVisible); } async removeContentPicker(contentPickerName: string) { @@ -667,7 +667,7 @@ export class ContentUiHelper extends UiBaseLocators { } async isMediaNameVisible(mediaName: string, isVisible: boolean = true) { - return expect(this.mediaCardItems.filter({hasText: mediaName})).toBeVisible({visible: isVisible}); + return await this.isVisible(this.mediaCardItems.filter({hasText: mediaName}), isVisible); } async clickResetFocalPointButton() { @@ -825,7 +825,7 @@ export class ContentUiHelper extends UiBaseLocators { } async isDocumentTypeNameVisible(contentName: string, isVisible: boolean = true) { - return expect(this.sidebarModal.getByText(contentName)).toBeVisible({visible: isVisible}); + return await this.isVisible(this.sidebarModal.getByText(contentName), isVisible); } async doesModalHaveText(text: string) { @@ -834,7 +834,7 @@ export class ContentUiHelper extends UiBaseLocators { // Collection tab async isTabNameVisible(tabName: string) { - return expect(this.tabItems.filter({hasText: tabName})).toBeVisible(); + return await this.isVisible(this.tabItems.filter({hasText: tabName})); } async doesDocumentHaveName(name: string) { @@ -882,11 +882,11 @@ export class ContentUiHelper extends UiBaseLocators { } async doesListViewHaveNoItemsInList() { - await expect(this.listView.filter({hasText: 'There are no items to show in the list.'})).toBeVisible(); + await this.isVisible(this.listView.filter({hasText: 'There are no items to show in the list.'})); } async doesContentListHaveNoItemsInList() { - await expect(this.umbDocumentCollection.filter({hasText: 'No items'})).toBeVisible(); + await this.isVisible(this.umbDocumentCollection.filter({hasText: 'No items'})); } async clickNameButtonInListView() { @@ -1042,7 +1042,7 @@ export class ContentUiHelper extends UiBaseLocators { } async isCaretButtonVisibleForContentName(contentName: string, isVisible: boolean = true) { - await expect(this.page.locator(`[label="${contentName}"]`).getByLabel('Expand child items for ')).toBeVisible({visible: isVisible}); + await this.isVisible(this.page.locator(`[label="${contentName}"]`).getByLabel('Expand child items for '), isVisible); } async reloadContentTree() { @@ -1116,10 +1116,10 @@ export class ContentUiHelper extends UiBaseLocators { } async isPermissionInActionsMenuVisible(permissionName: string, isVisible: boolean = true) { - await expect(this.actionsMenu.getByRole('button', { + await this.isVisible(this.actionsMenu.getByRole('button', { name: permissionName, exact: true - })).toBeVisible({visible: isVisible}); + }), isVisible); } async clickDocumentLinkButton() { @@ -1305,7 +1305,7 @@ export class ContentUiHelper extends UiBaseLocators { async doesBlockGridBlockWithAreaContainCreateLabel(blockWithAreaName: string, createLabel: string, index: number = 0) { const blockWithArea = this.blockGridEntry.locator(this.blockGridBlock.filter({hasText: blockWithAreaName})).nth(index); - return expect(blockWithArea.locator(this.blockGridAreasContainer).getByLabel(createLabel)).toBeVisible(); + return await this.isVisible(blockWithArea.locator(this.blockGridAreasContainer).getByLabel(createLabel)); } async doesPropertyContainValue(propertyName: string, value: string) { @@ -1329,11 +1329,11 @@ export class ContentUiHelper extends UiBaseLocators { } async doesClipboardHaveCopiedBlockWithName(contentName: string, propertyName: string, blockName: string, index: number = 0) { - await expect(this.clipboardEntryPicker.getByLabel(`${contentName} - ${propertyName} - ${blockName}`).nth(index)).toBeVisible(); + await this.isVisible(this.clipboardEntryPicker.getByLabel(`${contentName} - ${propertyName} - ${blockName}`).nth(index)); } async doesClipboardHaveCopiedBlocks(contentName: string, propertyName: string, index: number = 0) { - await expect(this.clipboardEntryPicker.getByLabel(`${contentName} - ${propertyName}`).nth(index)).toBeVisible(); + await this.isVisible(this.clipboardEntryPicker.getByLabel(`${contentName} - ${propertyName}`).nth(index)); } async doesClipboardContainCopiedBlocksCount(count: number) { @@ -1411,19 +1411,19 @@ export class ContentUiHelper extends UiBaseLocators { } async doesBlockEditorModalContainEditorSize(editorSize: string, elementName: string) { - await expect(this.backofficeModalContainer.locator(`[size="${editorSize}"]`).locator(`[headline="Add ${elementName}"]`)).toBeVisible(); + await this.isVisible(this.backofficeModalContainer.locator(`[size="${editorSize}"]`).locator(`[headline="Add ${elementName}"]`)); } async doesBlockEditorModalContainInline(richTextEditorAlias: string, elementName: string) { - await expect(this.page.getByTestId(`property:${richTextEditorAlias}`).locator(this.tiptapInput).locator(this.rteBlockInline)).toContainText(elementName); + await this.containsText(this.page.getByTestId(`property:${richTextEditorAlias}`).locator(this.tiptapInput).locator(this.rteBlockInline), elementName); } async doesBlockHaveBackgroundColor(elementName: string, backgroundColor: string) { - await expect(this.page.locator('umb-block-type-card', {hasText: elementName}).locator(`[style="background-color:${backgroundColor};"]`)).toBeVisible(); + await this.isVisible(this.page.locator('umb-block-type-card', {hasText: elementName}).locator(`[style="background-color:${backgroundColor};"]`)); } async doesBlockHaveIconColor(elementName: string, backgroundColor: string) { - await expect(this.page.locator('umb-block-type-card', {hasText: elementName}).locator(`[color="${backgroundColor}"]`)).toBeVisible(); + await this.isVisible(this.page.locator('umb-block-type-card', {hasText: elementName}).locator(`svg[fill="${backgroundColor}"]`)); } async addDocumentDomain(domainName: string, languageName: string) { @@ -1608,7 +1608,7 @@ export class ContentUiHelper extends UiBaseLocators { // Block Custom View async isBlockCustomViewVisible(blockCustomViewLocator: string, isVisible: boolean = true) { - await expect(this.page.locator(blockCustomViewLocator)).toBeVisible({visible: isVisible}); + await this.isVisible(this.page.locator(blockCustomViewLocator), isVisible); } async isSingleBlockElementVisible(isVisible: boolean = true) { From e6534c22c14c9d9d36e1c781b49ba9027c80c703 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 12 Dec 2025 00:17:57 +0700 Subject: [PATCH 23/34] Updated test helper to fix the failing tests --- lib/helpers/ConstantHelper.ts | 2 +- lib/helpers/LogViewerUiHelper.ts | 2 +- lib/helpers/UserUiHelper.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/helpers/ConstantHelper.ts b/lib/helpers/ConstantHelper.ts index 243c0a35..259099b0 100644 --- a/lib/helpers/ConstantHelper.ts +++ b/lib/helpers/ConstantHelper.ts @@ -4,7 +4,7 @@ short: 1000, medium: 5000, long: 10000, - veryLong: 30000, + veryLong: 20000, navigation: 30000, pageLoad: 60000 } diff --git a/lib/helpers/LogViewerUiHelper.ts b/lib/helpers/LogViewerUiHelper.ts index d0fedceb..1c423113 100644 --- a/lib/helpers/LogViewerUiHelper.ts +++ b/lib/helpers/LogViewerUiHelper.ts @@ -111,7 +111,7 @@ export class LogViewerUiHelper extends UiBaseLocators { } async removeSavedSearchByName(name: string) { - const removedSavedSearchWithNameLocator = this.page.locator('li').filter({hasText: name}).getByLabel('Remove saved search'); + const removedSavedSearchWithNameLocator = this.page.locator('.saved-search-item').filter({hasText: name}).getByLabel('Delete this search'); // The force click is necessary. await this.click(removedSavedSearchWithNameLocator, {force: true}); } diff --git a/lib/helpers/UserUiHelper.ts b/lib/helpers/UserUiHelper.ts index 78fd1ba2..775c0ca1 100644 --- a/lib/helpers/UserUiHelper.ts +++ b/lib/helpers/UserUiHelper.ts @@ -66,7 +66,7 @@ export class UserUiHelper extends UiBaseLocators { this.mediaStartNode = page.locator('umb-user-media-start-node'); this.usersMenu = page.locator('umb-menu').getByLabel('Users', {exact: true}); this.userBtn = page.locator('#collection-action-menu-popover').getByLabel('User', {exact: true}); - this.userGrid = page.locator('#user-grid'); + this.userGrid = page.locator('#card-grid'); this.apiUserBtn = page.locator('#collection-action-menu-popover').getByLabel('API User', {exact: true}); this.goToProfileBtn = page.getByLabel('Go to profile', {exact: true}); } From 4ad3f5c4c8802c0966292efe9494b5b196be18ce Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 12 Dec 2025 00:19:35 +0700 Subject: [PATCH 24/34] Bumped version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a08cf490..16e28b1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "17.1.0-beta", + "version": "17.1.0-beta.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@umbraco/playwright-testhelpers", - "version": "17.1.0-beta", + "version": "17.1.0-beta.1", "license": "MIT", "dependencies": { "@umbraco/json-models-builders": "2.0.42", diff --git a/package.json b/package.json index 5ea0e66e..bff6c3d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "17.1.0-beta", + "version": "17.1.0-beta.1", "description": "Test helpers for making playwright tests for Umbraco solutions", "main": "dist/lib/index.js", "files": [ From 61915c803e6d158770e9f234c6f2ae37137dce6c Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 12 Dec 2025 12:27:53 +0700 Subject: [PATCH 25/34] Fix locator selectors and add wait for publish confirmation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix isContentInTreeVisible to use getByLabel with exact match - Fix typo in isChildContentInTreeVisible selector (extra bracket) - Update doesBlockHaveIconColor to use color attribute selector - Add wait after clicking confirm to publish button 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- lib/helpers/ContentUiHelper.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index 7b93765a..961e0cdc 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -623,11 +623,11 @@ export class ContentUiHelper extends UiBaseLocators { } async isContentInTreeVisible(name: string, isVisible: boolean = true) { - await this.isVisible(this.documentTreeItem.locator('[label="' + name + '"]'), isVisible); + await this.isVisible(this.documentTreeItem.getByLabel(name, {exact: true}).first(), isVisible); } async isChildContentInTreeVisible(parentName: string, childName: string, isVisible: boolean = true) { - await this.isVisible(this.documentTreeItem.locator('[label="' + parentName + '"]').locator('[uui-menu-item[label="' + childName + '"]'), isVisible); + await this.isVisible(this.documentTreeItem.locator('[label="' + parentName + '"]').locator('uui-menu-item[label="' + childName + '"]'), isVisible); } async removeContentPicker(contentPickerName: string) { @@ -1423,7 +1423,7 @@ export class ContentUiHelper extends UiBaseLocators { } async doesBlockHaveIconColor(elementName: string, backgroundColor: string) { - await this.isVisible(this.page.locator('umb-block-type-card', {hasText: elementName}).locator(`svg[fill="${backgroundColor}"]`)); + await this.isVisible(this.page.locator('umb-block-type-card', {hasText: elementName}).locator(`[color="${backgroundColor}"]`)); } async addDocumentDomain(domainName: string, languageName: string) { @@ -1512,6 +1512,8 @@ export class ContentUiHelper extends UiBaseLocators { async clickConfirmToPublishButton() { await this.click(this.confirmToPublishBtn); + // Extra wait to ensure publish process starts + await this.waitForTimeout(ConstantHelper.wait.medium); } async clickPublishWithDescendantsButton() { From e26acc68a73dd8e83a1ad6893dfa39f49e952698 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 12 Dec 2025 12:28:26 +0700 Subject: [PATCH 26/34] Bumped version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 16e28b1c..0e07e0e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "17.1.0-beta.1", + "version": "17.1.0-beta.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@umbraco/playwright-testhelpers", - "version": "17.1.0-beta.1", + "version": "17.1.0-beta.2", "license": "MIT", "dependencies": { "@umbraco/json-models-builders": "2.0.42", diff --git a/package.json b/package.json index bff6c3d9..b83c8f6b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "17.1.0-beta.1", + "version": "17.1.0-beta.2", "description": "Test helpers for making playwright tests for Umbraco solutions", "main": "dist/lib/index.js", "files": [ From 01b5d9ddfea0dd530f0492399b7cf8171476bb7f Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 12 Dec 2025 12:58:26 +0700 Subject: [PATCH 27/34] Refactor new code to use BasePage methods --- lib/helpers/ContentUiHelper.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index 9de8a74d..75df75c8 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -847,8 +847,7 @@ export class ContentUiHelper extends UiBaseLocators { async clickTabWithName(tabName: string) { const tabLocator = this.tabItems.filter({hasText: tabName}); - await expect(tabLocator).toBeVisible(); - await tabLocator.click(); + await this.click(tabLocator); } async doesDocumentHaveName(name: string) { @@ -1733,24 +1732,22 @@ export class ContentUiHelper extends UiBaseLocators { async isInlineBlockPropertyVisible(propertyName: string, isVisible: boolean = true) { const propertyLocator = this.blockListEntry.locator(this.blockProperty).filter({hasText: propertyName}); - await expect(propertyLocator).toBeVisible({visible: isVisible}); + await this.isVisible(propertyLocator, isVisible); } async isInlineBlockPropertyVisibleForBlockWithName(blockName: string, propertyName: string, isVisible: boolean = true, index: number = 0) { const blockEntryLocator = this.blockListEntry.filter({hasText: blockName}).nth(index); const propertyLocator = blockEntryLocator.locator(this.blockProperty).filter({hasText: propertyName}); - await expect(propertyLocator).toBeVisible({visible: isVisible}); + await this.isVisible(propertyLocator, isVisible); } async enterInlineBlockPropertyValue(propertyName: string, value: string, index: number = 0) { const propertyLocator = this.blockListEntry.nth(index).locator(this.blockProperty).filter({hasText: propertyName}); - await expect(propertyLocator).toBeVisible(); - await propertyLocator.locator('input').clear(); - await propertyLocator.locator('input').fill(value); + await this.enterText(propertyLocator.locator('input'), value); } async doesInlineBlockPropertyHaveValue(propertyName: string, value: string, index: number = 0) { const propertyLocator = this.blockListEntry.nth(index).locator(this.blockProperty).filter({hasText: propertyName}).locator('input'); - await expect(propertyLocator).toHaveValue(value); + await this.hasValue(propertyLocator, value); } } \ No newline at end of file From 75ece7b24046d12ec392cfe26774cc5921794a26 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 12 Dec 2025 15:14:50 +0700 Subject: [PATCH 28/34] Updated locator to avoid flaky tests --- lib/helpers/ContentUiHelper.ts | 4 +++- lib/helpers/ProfilingUiHelper.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index 75df75c8..78af4996 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -247,7 +247,7 @@ export class ContentUiHelper extends UiBaseLocators { this.hostnameComboBox = this.hostNameItem.locator('[label="Culture"]').locator('uui-combobox-list-option'); this.saveModalBtn = this.sidebarModal.getByLabel('Save', {exact: true}); this.resetFocalPointBtn = page.getByLabel('Reset focal point'); - this.addNewHostnameBtn = page.getByLabel('Add new hostname'); + this.addNewHostnameBtn = page.locator('umb-property-layout[label="Hostnames"]').locator('[label="Add new hostname"]'); // List View this.enterNameInContainerTxt = this.container.getByTestId('input:entity-name').locator('#input'); this.listView = page.locator('umb-document-table-collection-view'); @@ -504,6 +504,8 @@ export class ContentUiHelper extends UiBaseLocators { async waitForContentToBeCreated() { await this.waitForLoadState(); + // Extra wait as content creation seems to take a bit longer sometimes + await this.waitForTimeout(ConstantHelper.wait.short); } async waitForContentToBeDeleted() { diff --git a/lib/helpers/ProfilingUiHelper.ts b/lib/helpers/ProfilingUiHelper.ts index eb8bb39e..0e0c5afe 100644 --- a/lib/helpers/ProfilingUiHelper.ts +++ b/lib/helpers/ProfilingUiHelper.ts @@ -10,7 +10,7 @@ export class ProfilingUiHelper extends UiBaseLocators { super(page); this.profilingTab = page.getByRole('tab', {name: 'Profiling'}); this.activateProfilerByDefaultToggle = page.locator("[label='Activate the profiler by default'] #toggle"); - this.activateProfilerByDefaultCheckbox = page.getByLabel('Activate the profiler by default'); + this.activateProfilerByDefaultCheckbox = page.locator("[label='Activate the profiler by default']"); } async clickProfilingTab() { From 94150429edc10e0bf71d5bc77e4e33ec1c942ed9 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 12 Dec 2025 15:15:37 +0700 Subject: [PATCH 29/34] Added more waits --- lib/helpers/DocumentTypeUiHelper.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/helpers/DocumentTypeUiHelper.ts b/lib/helpers/DocumentTypeUiHelper.ts index 67092a13..44b51c9f 100644 --- a/lib/helpers/DocumentTypeUiHelper.ts +++ b/lib/helpers/DocumentTypeUiHelper.ts @@ -1,4 +1,5 @@ -import {UiBaseLocators} from "./UiBaseLocators"; +import {ConstantHelper} from "./ConstantHelper"; +import {UiBaseLocators} from "./UiBaseLocators"; import {expect, Locator, Page} from "@playwright/test"; export class DocumentTypeUiHelper extends UiBaseLocators { @@ -84,6 +85,8 @@ export class DocumentTypeUiHelper extends UiBaseLocators { async waitForDocumentTypeToBeCreated() { await this.waitForLoadState(); + // Extra wait as document type creation seems to take a bit longer sometimes + await this.waitForTimeout(ConstantHelper.wait.short); } async waitForDocumentTypeToBeDeleted() { From 3a5538ceacce957aaeaaaf452ad410dea624a869 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 12 Dec 2025 15:16:34 +0700 Subject: [PATCH 30/34] Bumped version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0e07e0e9..6e1bdb4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "17.1.0-beta.2", + "version": "17.1.0-beta.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@umbraco/playwright-testhelpers", - "version": "17.1.0-beta.2", + "version": "17.1.0-beta.3", "license": "MIT", "dependencies": { "@umbraco/json-models-builders": "2.0.42", diff --git a/package.json b/package.json index b83c8f6b..020d28d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "17.1.0-beta.2", + "version": "17.1.0-beta.3", "description": "Test helpers for making playwright tests for Umbraco solutions", "main": "dist/lib/index.js", "files": [ From 079c5722f0b0001b53f609c76839a8b3a7140511 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 12 Dec 2025 16:54:42 +0700 Subject: [PATCH 31/34] Increase the default timeout --- lib/helpers/BasePage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helpers/BasePage.ts b/lib/helpers/BasePage.ts index 5c50c597..8858bcd6 100644 --- a/lib/helpers/BasePage.ts +++ b/lib/helpers/BasePage.ts @@ -36,7 +36,7 @@ export class BasePage { * @param options - Optional click configuration */ async click(locator: Locator, options?: { force?: boolean; timeout?: number }): Promise { - await expect(locator).toBeVisible({ timeout: options?.timeout ?? 5000 }); + await expect(locator).toBeVisible({ timeout: options?.timeout ?? 7000 }); await locator.click({ force: options?.force }); } From fc52864a29c2f316f602bba87542cf3a9ee24159 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 12 Dec 2025 16:54:53 +0700 Subject: [PATCH 32/34] Updated locator --- lib/helpers/ProfilingUiHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helpers/ProfilingUiHelper.ts b/lib/helpers/ProfilingUiHelper.ts index 0e0c5afe..f0a91b4f 100644 --- a/lib/helpers/ProfilingUiHelper.ts +++ b/lib/helpers/ProfilingUiHelper.ts @@ -10,7 +10,7 @@ export class ProfilingUiHelper extends UiBaseLocators { super(page); this.profilingTab = page.getByRole('tab', {name: 'Profiling'}); this.activateProfilerByDefaultToggle = page.locator("[label='Activate the profiler by default'] #toggle"); - this.activateProfilerByDefaultCheckbox = page.locator("[label='Activate the profiler by default']"); + this.activateProfilerByDefaultCheckbox = page.locator("[label='Activate the profiler by default'] input[type='checkbox']"); } async clickProfilingTab() { From 085f626a69c8e3b5a5415c993467f8ea94250baa Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 12 Dec 2025 16:55:03 +0700 Subject: [PATCH 33/34] Added more waits --- lib/helpers/ContentUiHelper.ts | 2 +- lib/helpers/UiBaseLocators.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/helpers/ContentUiHelper.ts b/lib/helpers/ContentUiHelper.ts index 78af4996..19a26839 100644 --- a/lib/helpers/ContentUiHelper.ts +++ b/lib/helpers/ContentUiHelper.ts @@ -402,7 +402,7 @@ export class ContentUiHelper extends UiBaseLocators { async goToContentWithName(contentName: string) { const contentWithNameLocator = this.menuItemTree.getByText(contentName, {exact: true}); - await this.click(contentWithNameLocator); + await this.click(contentWithNameLocator, {timeout: ConstantHelper.timeout.long}); } async clickActionsMenuForContent(name: string) { diff --git a/lib/helpers/UiBaseLocators.ts b/lib/helpers/UiBaseLocators.ts index f48e3f32..149c318e 100644 --- a/lib/helpers/UiBaseLocators.ts +++ b/lib/helpers/UiBaseLocators.ts @@ -498,6 +498,7 @@ export class UiBaseLocators extends BasePage { } else { menuItem = this.getMenuItemByLabel(name); } + await this.waitForVisible(menuItem, ConstantHelper.timeout.long); const isCaretButtonOpen = await menuItem.getAttribute('show-children'); if (isCaretButtonOpen === null) { await this.clickCaretButtonForName(name); @@ -769,7 +770,7 @@ export class UiBaseLocators extends BasePage { } async clickModalMenuItemWithName(name: string) { - await this.click(this.openedModal.locator(`uui-menu-item[label="${name}"]`)); + await this.click(this.openedModal.locator(`uui-menu-item[label="${name}"]`), {timeout: ConstantHelper.timeout.long}); } async isModalMenuItemWithNameDisabled(name: string) { From 8ab49df89728d56cd049fc3726c7ffc4e1498df4 Mon Sep 17 00:00:00 2001 From: Nhu Dinh Date: Fri, 12 Dec 2025 16:56:14 +0700 Subject: [PATCH 34/34] Bumped version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6e1bdb4e..e58b07c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "17.1.0-beta.3", + "version": "17.1.0-beta.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@umbraco/playwright-testhelpers", - "version": "17.1.0-beta.3", + "version": "17.1.0-beta.4", "license": "MIT", "dependencies": { "@umbraco/json-models-builders": "2.0.42", diff --git a/package.json b/package.json index 020d28d9..1d9f85ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@umbraco/playwright-testhelpers", - "version": "17.1.0-beta.3", + "version": "17.1.0-beta.4", "description": "Test helpers for making playwright tests for Umbraco solutions", "main": "dist/lib/index.js", "files": [