diff --git a/src/index.ts b/src/index.ts index 44881a01..92046ebc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -256,6 +256,7 @@ export {default as boOrdersViewBlockMessagesPage} from '@pages/BO/orders/view/bl export {default as boOrdersViewBlockPaymentsPage} from '@pages/BO/orders/view/blockPayments'; export {default as boOrdersViewBlockProductsPage} from '@pages/BO/orders/view/blockProducts'; export {default as boOrdersViewBlockTabListPage} from '@pages/BO/orders/view/blockTabList'; +export {default as boOrderMessagesPage} from '@pages/BO/customerService/orderMessages'; export {default as boOrderSettingsPage} from '@pages/BO/shopParameters/orderSettings'; export {default as boPaymentMethodsPage} from '@pages/BO/payment/paymentMethods'; export {default as boPerformancePage} from '@pages/BO/advancedParameters/performance'; diff --git a/src/interfaces/BO/customerService/orderMessages/index.ts b/src/interfaces/BO/customerService/orderMessages/index.ts new file mode 100644 index 00000000..7cf25e96 --- /dev/null +++ b/src/interfaces/BO/customerService/orderMessages/index.ts @@ -0,0 +1,20 @@ +import {BOBasePagePageInterface} from '@interfaces/BO'; +import {type Page} from '@playwright/test'; + +export interface BOOrderMessagesPageInterface extends BOBasePagePageInterface { + readonly pageTitle: string; + + deleteOrderMessage(page: Page, row?: number): Promise; + deleteWithBulkActions(page: Page): Promise; + filterTable(page: Page, filterBy: string, value: string): Promise; + getAllRowsColumnContent(page: Page, column: string): Promise; + getNumberOfElementInGrid(page: Page): Promise; + getTextColumnFromTable(page: Page, row: number, column: string): Promise; + goToAddNewOrderMessagePage(page: Page): Promise; + gotoEditOrderMessage(page: Page, row?: number): Promise; + paginationNext(page: Page): Promise; + paginationPrevious(page: Page): Promise; + resetAndGetNumberOfLines(page: Page): Promise; + selectPaginationLimit(page: Page, number: number): Promise; + sortTable(page: Page, sortBy: string, sortDirection?: string): Promise; +} diff --git a/src/pages/BO/customerService/orderMessages/index.ts b/src/pages/BO/customerService/orderMessages/index.ts new file mode 100644 index 00000000..9fc26280 --- /dev/null +++ b/src/pages/BO/customerService/orderMessages/index.ts @@ -0,0 +1,9 @@ +import {type BOOrderMessagesPageInterface} from '@interfaces/BO/customerService/orderMessages'; + +/* eslint-disable global-require, @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ +function requirePage(): BOOrderMessagesPageInterface { + return require('@versions/develop/pages/BO/customerService/orderMessages'); +} +/* eslint-enable global-require, @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ + +export default requirePage(); diff --git a/src/versions/develop/pages/BO/customerService/orderMessages/index.ts b/src/versions/develop/pages/BO/customerService/orderMessages/index.ts new file mode 100644 index 00000000..d5125bed --- /dev/null +++ b/src/versions/develop/pages/BO/customerService/orderMessages/index.ts @@ -0,0 +1,342 @@ +import {type BOOrderMessagesPageInterface} from '@interfaces/BO/customerService/orderMessages'; +import BOBasePage from '@pages/BO/BOBasePage'; +import {type Page} from '@playwright/test'; + +/** + * Order messages listing page, contains selectors and functions for the page + * @class + * @extends BOBasePage + */ +class BOOrderMessagesPage extends BOBasePage implements BOOrderMessagesPageInterface { + public readonly pageTitle: string; + + private readonly newOrderMessageLink: string; + + private readonly gridPanel: string; + + private readonly gridTable: string; + + private readonly gridHeaderTitle: string; + + private readonly filterColumn: (filterBy: string) => string; + + private readonly filterSearchButton: string; + + private readonly filterResetButton: string; + + private readonly tableBody: string; + + private readonly tableRow: (row: number) => string; + + private readonly tableEmptyRow: string; + + private readonly tableColumn: (row: number, column: string) => string; + + private readonly actionsColumn: (row: number) => string; + + private readonly editRowLink: (row: number) => string; + + private readonly dropdownToggleButton: (row: number) => string; + + private readonly dropdownToggleMenu: (row: number) => string; + + private readonly deleteRowLink: (row: number) => string; + + private readonly selectAllRowsLabel: string; + + private readonly bulkActionsToggleButton: string; + + private readonly bulkActionsDeleteButton: string; + + private readonly confirmDeleteModal: string; + + private readonly confirmDeleteButton: string; + + private readonly paginationLimitSelect: string; + + private readonly paginationLabel: string; + + private readonly paginationNextLink: string; + + private readonly paginationPreviousLink: string; + + private readonly tableHead: string; + + private readonly sortColumnDiv: (column: string) => string; + + private readonly sortColumnSpanButton: (column: string) => string; + + /** + * @constructs + * Setting up titles and selectors to use on order messages listing page + */ + constructor() { + super(); + + this.pageTitle = `Order messages • ${global.INSTALL.SHOP_NAME}`; + + // Selectors header + this.newOrderMessageLink = '#page-header-desc-configuration-add'; + + // Selectors grid panel + this.gridPanel = '#order_message_grid_panel'; + this.gridTable = '#order_message_grid_table'; + this.gridHeaderTitle = `${this.gridPanel} h3.card-header-title`; + // Filters + this.filterColumn = (filterBy: string) => `${this.gridTable} #order_message_${filterBy}`; + this.filterSearchButton = `${this.gridTable} .grid-search-button`; + this.filterResetButton = `${this.gridTable} .grid-reset-button`; + // Table rows and columns + this.tableBody = `${this.gridTable} tbody`; + this.tableRow = (row: number) => `${this.tableBody} tr:nth-child(${row})`; + this.tableEmptyRow = `${this.tableBody} tr.empty_row`; + this.tableColumn = (row: number, column: string) => `${this.tableRow(row)} td.column-${column}`; + // Actions buttons in Row + this.actionsColumn = (row: number) => `${this.tableRow(row)} td.column-actions`; + this.editRowLink = (row: number) => `${this.actionsColumn(row)} a.grid-edit-row-link`; + this.dropdownToggleButton = (row: number) => `${this.actionsColumn(row)} a.dropdown-toggle`; + this.dropdownToggleMenu = (row: number) => `${this.actionsColumn(row)} div.dropdown-menu`; + this.deleteRowLink = (row: number) => `${this.dropdownToggleMenu(row)} a.grid-delete-row-link`; + // Bulk Actions + this.selectAllRowsLabel = `${this.gridPanel} tr.column-filters .grid_bulk_action_select_all`; + this.bulkActionsToggleButton = `${this.gridPanel} button.js-bulk-actions-btn`; + this.bulkActionsDeleteButton = '#order_message_grid_bulk_action_delete_selection'; + // Delete modal + this.confirmDeleteModal = '#order_message-grid-confirm-modal'; + this.confirmDeleteButton = `${this.confirmDeleteModal} button.btn-confirm-submit`; + // Pagination selectors + this.paginationLimitSelect = '#paginator_select_page_limit'; + this.paginationLabel = `${this.gridPanel} .col-form-label`; + this.paginationNextLink = `${this.gridPanel} [data-role=next-page-link]`; + this.paginationPreviousLink = `${this.gridPanel} [data-role='previous-page-link']`; + // Sort Selectors + this.tableHead = `${this.gridPanel} thead`; + this.sortColumnDiv = (column: string) => `${this.tableHead} div.ps-sortable-column[data-sort-col-name='${column}']`; + this.sortColumnSpanButton = (column: string) => `${this.sortColumnDiv(column)} span.ps-sort`; + } + + /* Header Methods */ + /** + * Go to new order message page + * @param page {Page} Browser tab + * @return {Promise} + */ + async goToAddNewOrderMessagePage(page: Page): Promise { + await this.clickAndWaitForURL(page, this.newOrderMessageLink); + } + + /* Reset Methods */ + /** + * Reset filters in table + * @param page {Page} Browser tab + * @return {Promise} + */ + async resetFilter(page: Page): Promise { + if (await this.elementVisible(page, this.filterResetButton, 2000)) { + await this.clickAndWaitForLoadState(page, this.filterResetButton); + // Move the mouse to avoid the tooltip on first row + await page.mouse.move(0, 0); + await this.waitForHiddenSelector(page, this.filterResetButton, 2000); + } + } + + /** + * get number of elements in grid + * @param page {Page} Browser tab + * @returns {Promise} + */ + async getNumberOfElementInGrid(page: Page): Promise { + return this.getNumberFromText(page, this.gridHeaderTitle); + } + + /** + * Reset Filter And get number of elements in list + * @param page {Page} Browser tab + * @returns {Promise} + */ + async resetAndGetNumberOfLines(page: Page): Promise { + await this.resetFilter(page); + return this.getNumberOfElementInGrid(page); + } + + /* filter Methods */ + /** + * Filter Table + * @param page {Page} Browser tab + * @param filterBy {string} Column to filter with + * @param value {string|number} Value to put in filter + * @return {Promise} + */ + async filterTable(page: Page, filterBy: string, value: string): Promise { + await this.setValue(page, this.filterColumn(filterBy), value); + // click on search + await page.locator(this.filterSearchButton).click(); + await this.waitForVisibleSelector(page, this.filterResetButton); + } + + /* Column Methods */ + /** + * Edit order message + * @param page {Page} Browser tab + * @param row {number} Row to click on + * @return {Promise} + */ + async gotoEditOrderMessage(page: Page, row: number = 1): Promise { + await this.clickAndWaitForURL(page, this.editRowLink(row)); + } + + /** + * Delete Row in table + * @param page {Page} Browser tab + * @param row {number} Row to delete + * @returns {Promise} + */ + async deleteOrderMessage(page: Page, row: number = 1): Promise { + await Promise.all([ + page.locator(this.dropdownToggleButton(row)).click(), + this.waitForVisibleSelector( + page, + `${this.dropdownToggleButton(row)}[aria-expanded='true']`, + ), + ]); + + // Click on delete and wait for modal + await Promise.all([ + page.locator(this.deleteRowLink(row)).click(), + this.waitForVisibleSelector(page, `${this.confirmDeleteModal}.show`), + ]); + + // Confirm delete in modal + await this.confirmDeleteOrderMessages(page); + return this.getAlertSuccessBlockParagraphContent(page); + } + + /** + * Get text from a column + * @param page {Page} Browser tab + * @param row {number} Row to delete + * @param column {string} which column to get text from + * @returns {Promise} + */ + async getTextColumnFromTable(page: Page, row: number, column: string): Promise { + return this.getTextContent(page, this.tableColumn(row, column)); + } + + /* Bulk Actions Methods */ + /** + * Delete with bulk actions + * @param page {Page} Browser tab + * @returns {Promise} + */ + async deleteWithBulkActions(page: Page): Promise { + // Click on Select All + await Promise.all([ + page.locator(this.selectAllRowsLabel).evaluate((el: HTMLElement) => el.click()), + this.waitForVisibleSelector(page, `${this.bulkActionsToggleButton}:not([disabled])`), + ]); + // Click on Button Bulk actions + await Promise.all([ + page.locator(this.bulkActionsToggleButton).click(), + this.waitForVisibleSelector(page, `${this.bulkActionsToggleButton}[aria-expanded='true']`), + ]); + // Click on delete and wait for modal + await Promise.all([ + page.locator(this.bulkActionsDeleteButton).click(), + this.waitForVisibleSelector(page, `${this.confirmDeleteModal}.show`), + ]); + // Click on delete and wait for modal + await this.confirmDeleteOrderMessages(page); + return this.getAlertSuccessBlockParagraphContent(page); + } + + /** + * Confirm delete in modal + * @param page {Page} Browser tab + * @return {Promise} + */ + async confirmDeleteOrderMessages(page: Page): Promise { + await this.clickAndWaitForURL(page, this.confirmDeleteButton); + } + + /* Pagination methods */ + /** + * Get pagination label + * @param page {Page} Browser tab + * @returns {Promise} + */ + getPaginationLabel(page: Page): Promise { + return this.getTextContent(page, this.paginationLabel); + } + + /** + * Select pagination limit + * @param page {Page} Browser tab + * @param number {number} Pagination value to select + * @returns {Promise} + */ + async selectPaginationLimit(page: Page, number: number): Promise { + await this.selectByVisibleText(page, this.paginationLimitSelect, number); + return this.getPaginationLabel(page); + } + + /** + * Click on next + * @param page {Page} Browser tab + * @returns {Promise} + */ + async paginationNext(page: Page): Promise { + await this.clickAndWaitForURL(page, this.paginationNextLink); + return this.getPaginationLabel(page); + } + + /** + * Click on previous + * @param page {Page} Browser tab + * @returns {Promise} + */ + async paginationPrevious(page: Page): Promise { + await this.clickAndWaitForURL(page, this.paginationPreviousLink); + return this.getPaginationLabel(page); + } + + // Sort methods + /** + * Get content from all rows + * @param page {Page} Browser tab + * @param column {string} Column to get text from + * @return {Promise>} + */ + async getAllRowsColumnContent(page: Page, column: string): Promise { + const rowsNumber = await this.getNumberOfElementInGrid(page); + const allRowsContentTable: string[] = []; + + for (let i: number = 1; i <= rowsNumber; i++) { + const rowContent = await this.getTextContent(page, this.tableColumn(i, column)); + allRowsContentTable.push(rowContent); + } + + return allRowsContentTable; + } + + /** + * Sort table + * @param page {Page} Browser tab + * @param sortBy {string} Column to sort with + * @param sortDirection {string} Sort direction "asc"/"desc + * @return {Promise} + */ + async sortTable(page: Page, sortBy: string, sortDirection: string = 'asc'): Promise { + const sortColumnDiv = `${this.sortColumnDiv(sortBy)}[data-sort-direction='${sortDirection}']`; + const sortColumnSpanButton = this.sortColumnSpanButton(sortBy); + + let i: number = 0; + while (await this.elementNotVisible(page, sortColumnDiv, 2000) && i < 2) { + await this.clickAndWaitForURL(page, sortColumnSpanButton); + i += 1; + } + + await this.waitForVisibleSelector(page, sortColumnDiv, 20000); + } +} + +module.exports = new BOOrderMessagesPage();