diff --git a/src/pages/BO/shopParameters/stores/create.ts b/src/pages/BO/shopParameters/stores/create.ts index 933588ba9..e987ecc68 100644 --- a/src/pages/BO/shopParameters/stores/create.ts +++ b/src/pages/BO/shopParameters/stores/create.ts @@ -1,7 +1,14 @@ import type {BOStoresCreatePageInterface} from '@interfaces/BO/shopParameters/stores/create'; +import testContext from '@utils/test'; +import semver from 'semver'; + +const psVersion = testContext.getPSVersion(); /* eslint-disable global-require, @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ function requirePage(): BOStoresCreatePageInterface { + if (semver.lt(psVersion, '9.3.0')) { + return require('@versions/9.2/pages/BO/shopParameters/stores/create'); + } return require('@versions/develop/pages/BO/shopParameters/stores/create'); } /* eslint-enable global-require, @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ diff --git a/src/pages/BO/shopParameters/stores/index.ts b/src/pages/BO/shopParameters/stores/index.ts index 9f9422bd4..cd0f99ce4 100644 --- a/src/pages/BO/shopParameters/stores/index.ts +++ b/src/pages/BO/shopParameters/stores/index.ts @@ -1,7 +1,14 @@ import type {BOStoresPageInterface} from '@interfaces/BO/shopParameters/stores'; +import testContext from '@utils/test'; +import semver from 'semver'; + +const psVersion = testContext.getPSVersion(); /* eslint-disable global-require, @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ function requirePage(): BOStoresPageInterface { + if (semver.lt(psVersion, '9.3.0')) { + return require('@versions/9.2/pages/BO/shopParameters/stores'); + } return require('@versions/develop/pages/BO/shopParameters/stores'); } /* eslint-enable global-require, @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ diff --git a/src/versions/9.2/pages/BO/shopParameters/stores/create.ts b/src/versions/9.2/pages/BO/shopParameters/stores/create.ts new file mode 100644 index 000000000..b2be19f05 --- /dev/null +++ b/src/versions/9.2/pages/BO/shopParameters/stores/create.ts @@ -0,0 +1,249 @@ +import type FakerStore from '@data/faker/store'; +import {type BOStoresCreatePageInterface} from '@interfaces/BO/shopParameters/stores/create'; +import BOBasePage from '@pages/BO/BOBasePage'; +import {type Page} from '@playwright/test'; + +/** + * Add store page, contains selectors and functions for the page + * @class + * @extends BOBasePage + */ +class BOStoresCreatePage extends BOBasePage implements BOStoresCreatePageInterface { + public readonly pageTitleCreate: string; + + public readonly pageTitleEdit: string; + + private readonly storeForm: string; + + private readonly nameInput: string; + + private readonly address1Input: string; + + private readonly address2Input: string; + + private readonly postcodeInput: string; + + private readonly cityInput: string; + + private readonly countrySelect: string; + + private readonly stateSelect: string; + + private readonly latitudeInput: string; + + private readonly longitudeInput: string; + + private readonly phoneInput: string; + + private readonly faxInput: string; + + private readonly emailInput: string; + + private readonly noteTextarea: string; + + private readonly statusToggle: (toggle: string) => string; + + private readonly pictureInput: string; + + private readonly hoursInput: (pos: number, languageId: string) => string; + + private readonly saveButton: string; + + /** + * @constructs + * Setting up titles and selectors to use on add store page + */ + constructor() { + super(); + + this.pageTitleCreate = 'Stores > Add new •'; + this.pageTitleEdit = 'Stores > Edit:'; + + // Form selectors + this.storeForm = '#store_form'; + this.nameInput = '#name'; + this.address1Input = '#address1'; + this.address2Input = '#address2'; + this.postcodeInput = '#postcode'; + this.cityInput = '#city'; + this.countrySelect = '#id_country'; + this.stateSelect = '#id_state'; + this.latitudeInput = '#latitude'; + this.longitudeInput = '#longitude'; + this.phoneInput = '#phone'; + this.faxInput = '#fax'; + this.emailInput = '#email'; + this.noteTextarea = '#note_1'; + this.statusToggle = (toggle: string) => `${this.storeForm} #active_${toggle}`; + this.pictureInput = `${this.storeForm} #image`; + this.hoursInput = (pos: number, languageId: string) => `input[name='hours[${pos}][${languageId}]']`; + this.saveButton = '#store_form_submit_btn'; + this.alertSuccessBlockParagraph = '.alert-success'; + } + + /* Methods */ + + /** + * Fill creation / edition form for store and save it + * @param page {Page} Browser tab + * @param storeData {FakerStore} Data to set on store form + * @return {Promise} + */ + async createEditStore(page: Page, storeData: FakerStore): Promise { + // Set name + await this.setValue(page, `${this.nameInput}_1`, storeData.name); + + // Set address inputs + await this.setValue(page, `${this.address1Input}_1`, storeData.address1); + await this.setValue(page, `${this.address2Input}_1`, storeData.address2); + await this.setValue(page, this.postcodeInput, storeData.postcode); + await this.setValue(page, this.cityInput, storeData.city); + await this.selectByVisibleText(page, this.countrySelect, storeData.country); + await this.setValue(page, this.latitudeInput, storeData.latitude); + await this.setValue(page, this.longitudeInput, storeData.longitude); + + // Set phone inputs + await this.setValue(page, this.phoneInput, storeData.phone); + await this.setValue(page, this.faxInput, storeData.fax); + + // Set email and notes inputs + await this.setValue(page, this.emailInput, storeData.email); + await this.setValue(page, this.noteTextarea, storeData.note); + + // Set store status + await this.setChecked(page, this.statusToggle(storeData.status ? 'on' : 'off')); + + // Set store picture + if (storeData.picture) { + await this.uploadFile(page, this.pictureInput, storeData.picture); + } + + // Set opening hours + for (let day:number = 1; day <= 7; day++) { + await this.setValue(page, this.hoursInput(day, '1'), storeData.hours[day - 1]); + } + + // Save store + await this.clickAndWaitForURL(page, this.saveButton); + + // Return successful message + return this.getAlertSuccessBlockParagraphContent(page); + } + + /** + * Get Inputs values + * @param page {Page} + * @param inputName {string} + * @param languageId {string | undefined} + */ + async getInputValue(page: Page, inputName: string, languageId?: string): Promise { + let selector; + + switch (inputName) { + case 'name': + selector = `${this.nameInput}_${languageId}`; + break; + + case 'address1': + selector = `${this.address1Input}_${languageId}`; + break; + + case 'address2': + selector = `${this.address2Input}_${languageId}`; + break; + + case 'postcode': + selector = this.postcodeInput; + break; + + case 'city': + selector = this.cityInput; + break; + + case 'latitude': + selector = this.latitudeInput; + break; + + case 'longitude': + selector = this.longitudeInput; + break; + + case 'phone': + selector = this.phoneInput; + break; + + case 'fax': + selector = this.faxInput; + break; + + case 'email': + selector = this.emailInput; + break; + + case 'monday': + selector = this.hoursInput(1, languageId === undefined ? '1' : languageId); + break; + + case 'tuesday': + selector = this.hoursInput(2, languageId === undefined ? '1' : languageId); + break; + + case 'wednesday': + selector = this.hoursInput(3, languageId === undefined ? '1' : languageId); + break; + + case 'thursday': + selector = this.hoursInput(4, languageId === undefined ? '1' : languageId); + break; + + case 'friday': + selector = this.hoursInput(5, languageId === undefined ? '1' : languageId); + break; + + case 'saturday': + selector = this.hoursInput(6, languageId === undefined ? '1' : languageId); + break; + + case 'sunday': + selector = this.hoursInput(7, languageId === undefined ? '1' : languageId); + break; + default: + throw new Error(`Column ${inputName} was not found`); + } + + return this.getAttributeContent(page, selector, 'value'); + } + + /** + * Get value of a select + * @param page {Page} + * @param name {string} + */ + async getSelectValue(page: Page, name: string): Promise { + let selector: string; + + switch (name) { + case 'id_country': + selector = this.countrySelect; + break; + case 'id_state': + selector = this.stateSelect; + break; + default: + throw new Error(`Field ${name} was not found`); + } + + return page.locator(selector).evaluate((node: HTMLSelectElement) => node.value); + } + + /** + * Get Toggle value + * @param page {Page} + * @param toggle {string} + */ + async isActive(page: Page, toggle: string): Promise { + return this.isChecked(page, this.statusToggle(toggle)); + } +} + +module.exports = new BOStoresCreatePage(); diff --git a/src/versions/9.2/pages/BO/shopParameters/stores/index.ts b/src/versions/9.2/pages/BO/shopParameters/stores/index.ts new file mode 100644 index 000000000..ff7242ae8 --- /dev/null +++ b/src/versions/9.2/pages/BO/shopParameters/stores/index.ts @@ -0,0 +1,623 @@ +import type FakerStore from '@data/faker/store'; +import {type BOStoresPageInterface} from '@interfaces/BO/shopParameters/stores'; +import BOBasePage from '@pages/BO/BOBasePage'; +import {type Page} from '@playwright/test'; + +/** + * Stores page, contains selectors and functions for the page + * @class + * @extends BOBasePage + */ +class BOStoresPage extends BOBasePage implements BOStoresPageInterface { + public readonly pageTitle: string; + + public readonly contactFormSuccessfulUpdateMessage: string; + + private readonly newStoreLink: string; + + private readonly gridForm: string; + + private readonly gridTableHeaderTitle: string; + + private readonly gridTableNumberOfTitlesSpan: string; + + private readonly gridTable: string; + + private readonly tableHead: string; + + private readonly sortColumnDiv: (column: number) => string; + + private readonly sortColumnSpanButton: (column: number) => string; + + private readonly filterRow: string; + + private readonly filterColumn: (filterBy: string) => string; + + private readonly filterSearchButton: string; + + private readonly filterResetButton: string; + + private readonly tableBody: string; + + private readonly tableBodyRows: string; + + private readonly tableBodyRow: (row: number) => string; + + private readonly tableBodyColumn: (row: number) => string; + + private readonly tableColumnId: (row: number) => string; + + private readonly tableColumnName: (row: number) => string; + + private readonly tableColumnAddress: (row: number) => string; + + private readonly tableColumnCity: (row: number) => string; + + private readonly tableColumnPostalCode: (row: number) => string; + + private readonly tableColumnState: (row: number) => string; + + private readonly tableColumnCountry: (row: number) => string; + + private readonly tableColumnPhone: (row: number) => string; + + private readonly tableColumnFax: (row: number) => string; + + private readonly tableColumnStatus: (row: number) => string; + + private readonly tableColumnActions: (row: number) => string; + + private readonly tableColumnActionsEditLink: (row: number) => string; + + private readonly tableColumnActionsToggleButton: (row: number) => string; + + private readonly tableColumnActionsDropdownMenu: (row: number) => string; + + private readonly tableColumnActionsDeleteLink: (row: number) => string; + + private readonly deleteModalButtonYes: string; + + private readonly bulkActionBlock: string; + + private readonly bulkActionMenuButton: string; + + private readonly bulkActionDropdownMenu: string; + + private readonly selectAllLink: string; + + private readonly enableSelectionLink: string; + + private readonly disableSelectionLink: string; + + private readonly bulkDeleteLink: string; + + private readonly paginationActiveLabel: string; + + private readonly paginationDiv: string; + + private readonly paginationDropdownButton: string; + + private readonly paginationItems: (number: number) => string; + + private readonly paginationPreviousLink: string; + + private readonly paginationNextLink: string; + + private readonly contactDetailsForm: string; + + private readonly nameInput: string; + + private readonly emailInput: string; + + private readonly registrationNumberTextarea: string; + + private readonly address1Input: string; + + private readonly address2Input: string; + + private readonly postcodeInput: string; + + private readonly cityInput: string; + + private readonly countrySelect: string; + + private readonly phoneInput: string; + + private readonly faxInput: string; + + private readonly saveButton: string; + + /** + * @constructs + * Setting up titles and selectors to use on stores page + */ + constructor() { + super(); + + this.pageTitle = 'Stores •'; + this.contactFormSuccessfulUpdateMessage = 'The settings have been successfully updated.'; + + this.alertSuccessBlockParagraph = '.alert-success'; + + // Header selectors + this.newStoreLink = 'a[data-role=page-header-desc-store-link]'; + + // Form selectors + this.gridForm = '#form-store'; + this.gridTableHeaderTitle = `${this.gridForm} .panel-heading`; + this.gridTableNumberOfTitlesSpan = `${this.gridTableHeaderTitle} span.badge`; + + // Table selectors + this.gridTable = '#table-store'; + + // Sort selectors + this.tableHead = `${this.gridTable} thead`; + this.sortColumnDiv = (column: number) => `${this.tableHead} th:nth-child(${column})`; + this.sortColumnSpanButton = (column: number) => `${this.sortColumnDiv(column)} span.ps-sort`; + + // Filter selectors + this.filterRow = `${this.gridTable} tr.filter`; + this.filterColumn = (filterBy: string) => `${this.filterRow} [name='storeFilter_${filterBy}']`; + this.filterSearchButton = '#submitFilterButtonstore'; + this.filterResetButton = 'button[name=\'submitResetstore\']'; + + // Table body selectors + this.tableBody = `${this.gridTable} tbody`; + this.tableBodyRows = `${this.tableBody} tr`; + this.tableBodyRow = (row: number) => `${this.tableBodyRows}:nth-child(${row})`; + this.tableBodyColumn = (row: number) => `${this.tableBodyRow(row)} td`; + + // Columns selectors + this.tableColumnId = (row: number) => `${this.tableBodyColumn(row)}:nth-child(2)`; + this.tableColumnName = (row: number) => `${this.tableBodyColumn(row)}:nth-child(3)`; + this.tableColumnAddress = (row: number) => `${this.tableBodyColumn(row)}:nth-child(4)`; + this.tableColumnCity = (row: number) => `${this.tableBodyColumn(row)}:nth-child(5)`; + this.tableColumnPostalCode = (row: number) => `${this.tableBodyColumn(row)}:nth-child(6)`; + this.tableColumnState = (row: number) => `${this.tableBodyColumn(row)}:nth-child(7)`; + this.tableColumnCountry = (row: number) => `${this.tableBodyColumn(row)}:nth-child(8)`; + this.tableColumnPhone = (row: number) => `${this.tableBodyColumn(row)}:nth-child(9)`; + this.tableColumnFax = (row: number) => `${this.tableBodyColumn(row)}:nth-child(10)`; + this.tableColumnStatus = (row: number) => `${this.tableBodyColumn(row)}:nth-child(11) a`; + + // Row actions selectors + this.tableColumnActions = (row: number) => `${this.tableBodyColumn(row)} .btn-group-action`; + this.tableColumnActionsEditLink = (row: number) => `${this.tableColumnActions(row)} a.edit`; + this.tableColumnActionsToggleButton = (row: number) => `${this.tableColumnActions(row)} button.dropdown-toggle`; + this.tableColumnActionsDropdownMenu = (row: number) => `${this.tableColumnActions(row)} .dropdown-menu`; + this.tableColumnActionsDeleteLink = (row: number) => `${this.tableColumnActionsDropdownMenu(row)} a.delete`; + + // Confirmation modal + this.deleteModalButtonYes = '#popup_ok'; + + // Bulk actions selectors + this.bulkActionBlock = 'div.bulk-actions'; + this.bulkActionMenuButton = '#bulk_action_menu_store'; + this.bulkActionDropdownMenu = `${this.bulkActionBlock} ul.dropdown-menu`; + this.selectAllLink = `${this.bulkActionDropdownMenu} li:nth-child(1)`; + this.enableSelectionLink = `${this.bulkActionDropdownMenu} li:nth-child(4)`; + this.disableSelectionLink = `${this.bulkActionDropdownMenu} li:nth-child(5)`; + this.bulkDeleteLink = `${this.bulkActionDropdownMenu} li:nth-child(7)`; + + // Pagination selectors + this.paginationActiveLabel = `${this.gridForm} ul.pagination.pull-right li.active a`; + this.paginationDiv = `${this.gridForm} .pagination`; + this.paginationDropdownButton = `${this.paginationDiv} .dropdown-toggle`; + this.paginationItems = (number: number) => `${this.gridForm} .dropdown-menu a[data-items='${number}']`; + this.paginationPreviousLink = `${this.gridForm} .icon-angle-left`; + this.paginationNextLink = `${this.gridForm} .icon-angle-right`; + + // Contact details form + this.contactDetailsForm = '#store_fieldset_contact'; + this.nameInput = `${this.contactDetailsForm} input[name='PS_SHOP_NAME']`; + this.emailInput = `${this.contactDetailsForm} input[name='PS_SHOP_EMAIL']`; + this.registrationNumberTextarea = '#conf_id_PS_SHOP_DETAILS textarea[name=\'PS_SHOP_DETAILS\']'; + this.address1Input = `${this.contactDetailsForm} input[name='PS_SHOP_ADDR1']`; + this.address2Input = `${this.contactDetailsForm} input[name='PS_SHOP_ADDR2']`; + this.postcodeInput = `${this.contactDetailsForm} input[name='PS_SHOP_CODE']`; + this.cityInput = `${this.contactDetailsForm} input[name='PS_SHOP_CITY']`; + this.countrySelect = '#PS_SHOP_COUNTRY_ID'; + this.phoneInput = `${this.contactDetailsForm} input[name='PS_SHOP_PHONE']`; + this.faxInput = `${this.contactDetailsForm} input[name='PS_SHOP_FAX']`; + this.saveButton = `${this.contactDetailsForm} button[name='submitOptionsstore']`; + } + + /* Header methods */ + /** + * Go to new store page + * @param page {Page} Browser tab + * @return {Promise} + */ + async goToNewStorePage(page: Page): Promise { + await this.clickAndWaitForURL(page, this.newStoreLink); + } + + /* Filter methods */ + + /** + * Get Number of stores + * @param page {Page} Browser tab + * @return {Promise} + */ + async getNumberOfElementInGrid(page: Page): Promise { + return this.getNumberFromText(page, this.gridTableNumberOfTitlesSpan); + } + + /** + * Reset all filters + * @param page {Page} Browser tab + * @return {Promise} + */ + async resetFilter(page: Page): Promise { + if (!(await this.elementNotVisible(page, this.filterResetButton, 2000))) { + await this.clickAndWaitForURL(page, this.filterResetButton); + } + await this.waitForVisibleSelector(page, this.filterSearchButton, 2000); + } + + /** + * Reset and get number of stores + * @param page {Page} Browser tab + * @return {Promise} + */ + async resetAndGetNumberOfLines(page: Page): Promise { + await this.resetFilter(page); + return this.getNumberOfElementInGrid(page); + } + + /** + * Filter stores + * @param page {Page} Browser tab + * @param filterType {string} Type of filter (input/select) + * @param filterBy {string} Column to filter with + * @param value {string} Value to filter + * @return {Promise} + */ + async filterTable(page: Page, filterType: string, filterBy: string, value: string): Promise { + const currentUrl: string = page.url(); + + switch (filterType) { + case 'input': + await this.setValue(page, this.filterColumn(filterBy), value.toString()); + await this.clickAndWaitForURL(page, this.filterSearchButton); + break; + + case 'select': + await Promise.all([ + page.waitForURL((url: URL): boolean => url.toString() !== currentUrl), + this.selectByVisibleText(page, this.filterColumn(filterBy), value === '1' ? 'Yes' : 'No'), + ]); + break; + + default: + throw new Error(`Filter ${filterBy} was not found`); + } + } + + /* Column methods */ + + /** + * Get text from column in table + * @param page {Page} Browser tab + * @param row {number} Row on table + * @param columnName {string} Column name of the value to return + * @return {Promise} + */ + async getTextColumn(page: Page, row: number, columnName: string): Promise { + let columnSelector; + + switch (columnName) { + case 'id_store': + columnSelector = this.tableColumnId(row); + break; + + case 'sl!name': + columnSelector = this.tableColumnName(row); + break; + + case 'sl!address1': + columnSelector = this.tableColumnAddress(row); + break; + + case 'city': + columnSelector = this.tableColumnCity(row); + break; + + case 'postcode': + columnSelector = this.tableColumnPostalCode(row); + break; + + case 'st!name': + columnSelector = this.tableColumnState(row); + break; + + case 'cl!name': + columnSelector = this.tableColumnCountry(row); + break; + + case 'phone': + columnSelector = this.tableColumnPhone(row); + break; + + case 'fax': + columnSelector = this.tableColumnFax(row); + break; + + default: + throw new Error(`Column ${columnName} was not found`); + } + + return this.getTextContent(page, columnSelector); + } + + /** + * Get column content from all rows + * @param page {Page} Browser tab + * @param columnName {string} Column name of the value to return + * @return {Promise>} + */ + async getAllRowsColumnContent(page: Page, columnName: string): Promise { + const rowsNumber = await this.getNumberOfElementInGrid(page); + const allRowsContentTable: string[] = []; + + // Get text column from each row + for (let i = 1; i <= rowsNumber; i++) { + const rowContent = await this.getTextColumn(page, i, columnName); + allRowsContentTable.push(rowContent); + } + + return allRowsContentTable; + } + + /** + * Get Store status + * @param page {Page} Browser tab + * @param row {number} Row on table + * @return {Promise} + */ + async getStoreStatus(page: Page, row: number): Promise { + return this.elementVisible(page, `${this.tableColumnStatus(row)}.action-enabled`, 1000); + } + + /** + * Set store status by clicking on column status + * @param page {Page} Browser tab + * @param row {number} Row on table + * @param wantedStatus {boolean} True if we need to enable status + * @return {Promise} + */ + async setStoreStatus(page: Page, row: number, wantedStatus: boolean): Promise { + const actualStatus = await this.getStoreStatus(page, row); + + if (actualStatus !== wantedStatus) { + await page.locator(this.tableColumnStatus(row)).click(); + } + } + + /** + * Go to edit store page + * @param page {Page} Browser tab + * @param row {number} Row on table + * @return {Promise} + */ + async gotoEditStorePage(page: Page, row: number): Promise { + // Special case where we wait for networkidle because the list of states in the select is loaded by ajax + await this.clickAndWaitForURL(page, this.tableColumnActionsEditLink(row), 'networkidle'); + } + + /** + * Delete store from row + * @param page {Page} Browser tab + * @param row {number} Row on table + * @return {Promise} + */ + async deleteStore(page: Page, row: number): Promise { + await Promise.all([ + page.locator(this.tableColumnActionsToggleButton(row)).click(), + this.waitForVisibleSelector(page, this.tableColumnActionsDeleteLink(row)), + ]); + + await page.locator(this.tableColumnActionsDeleteLink(row)).click(); + + // Confirm delete action + await this.clickAndWaitForURL(page, this.deleteModalButtonYes); + + // Get successful message + return this.getAlertSuccessBlockParagraphContent(page); + } + + /* Bulk actions methods */ + + /** + * Select all rows for bulk action + * @param page {Page} Browser tab + * @return {Promise} + */ + async selectAllRow(page: Page): Promise { + await Promise.all([ + page.locator(this.bulkActionMenuButton).click(), + this.waitForVisibleSelector(page, this.selectAllLink), + ]); + + await Promise.all([ + page.locator(this.selectAllLink).click(), + this.waitForHiddenSelector(page, this.selectAllLink), + ]); + } + + /** + * Enable / disable stores by bulk actions + * @param page {Page} Browser tab + * @param statusWanted {boolean} True if we need to enable status + * @return {Promise} + */ + async bulkUpdateStoreStatus(page: Page, statusWanted: boolean): Promise { + // Select all rows + await this.selectAllRow(page); + + // Perform bulk update status + await Promise.all([ + page.locator(this.bulkActionMenuButton).click(), + this.waitForVisibleSelector( + page, + this.enableSelectionLink, + ), + ]); + + await this.clickAndWaitForURL( + page, + statusWanted ? this.enableSelectionLink : this.disableSelectionLink, + ); + } + + /** + * Bulk delete stores + * @param page {Page} Browser tab + * @return {Promise} + */ + async bulkDeleteStores(page: Page): Promise { + // To confirm bulk delete action with dialog + await this.dialogListener(page, true); + + // Select all rows + await this.selectAllRow(page); + + // Perform delete + await Promise.all([ + page.locator(this.bulkActionMenuButton).click(), + this.waitForVisibleSelector(page, this.bulkDeleteLink), + ]); + + await this.clickAndWaitForURL(page, this.bulkDeleteLink); + + // Return successful message + return this.getAlertSuccessBlockParagraphContent(page); + } + + /* Sort functions */ + /** + * Sort table by clicking on column name + * @param page {Page} Browser tab + * @param sortBy {string} Column name to sort with + * @param sortDirection {string} Sort direction by asc or desc + * @return {Promise} + */ + async sortTable(page: Page, sortBy: string, sortDirection: string): Promise { + let columnSelector; + + switch (sortBy) { + case 'id_store': + columnSelector = this.sortColumnDiv(2); + break; + + case 'sl!name': + columnSelector = this.sortColumnDiv(3); + break; + + case 'sl!address1': + columnSelector = this.sortColumnDiv(4); + break; + + case 'city': + columnSelector = this.sortColumnDiv(5); + break; + + case 'postcode': + columnSelector = this.sortColumnDiv(6); + break; + + case 'st!name': + columnSelector = this.sortColumnDiv(7); + break; + + case 'cl!name': + columnSelector = this.sortColumnDiv(8); + break; + + default: + throw new Error(`Column ${sortBy} was not found`); + } + + const sortColumnButton = `${columnSelector} i.icon-caret-${sortDirection}`; + await this.clickAndWaitForURL(page, sortColumnButton); + } + + /* Form functions */ + /** + * Se contact details + * @param page {Page} Browser tab + * @param storeContactData {FakerStore} Store contact data to set on contact detail form + * @returns {Promise} + */ + async setContactDetails(page: Page, storeContactData: FakerStore): Promise { + // Set name + await this.setValue(page, this.nameInput, storeContactData.name); + + // Set email and registration number inputs + await this.setValue(page, this.emailInput, storeContactData.email); + await this.setValue(page, this.registrationNumberTextarea, storeContactData.registrationNumber); + + // Set address inputs + await this.setValue(page, this.address1Input, storeContactData.address1); + await this.setValue(page, this.address2Input, storeContactData.address2); + await this.setValue(page, this.postcodeInput, storeContactData.postcode); + await this.setValue(page, this.cityInput, storeContactData.city); + await this.selectByVisibleText(page, this.countrySelect, storeContactData.country); + + // Set phone inputs + await this.setValue(page, this.phoneInput, storeContactData.phone); + await this.setValue(page, this.faxInput, storeContactData.fax); + + // Save contact details + await page.locator(this.saveButton).click(); + + // Return successful message + return this.getAlertSuccessBlockParagraphContent(page); + } + + /* Pagination methods */ + /** + * Get pagination label + * @param page {Page} Browser tab + * @return {Promise} + */ + async getPaginationLabel(page: Page): Promise { + return this.getTextContent(page, this.paginationActiveLabel); + } + + /** + * Select pagination limit + * @param page {Page} Browser tab + * @param number {number} Pagination limit number to select + * @returns {Promise} + */ + async selectPaginationLimit(page: Page, number: number): Promise { + await this.waitForSelectorAndClick(page, this.paginationDropdownButton); + await this.clickAndWaitForURL(page, this.paginationItems(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); + } +} + +module.exports = new BOStoresPage(); diff --git a/src/versions/develop/pages/BO/shopParameters/stores/create.ts b/src/versions/develop/pages/BO/shopParameters/stores/create.ts index b2be19f05..3495ebb1c 100644 --- a/src/versions/develop/pages/BO/shopParameters/stores/create.ts +++ b/src/versions/develop/pages/BO/shopParameters/stores/create.ts @@ -15,11 +15,11 @@ class BOStoresCreatePage extends BOBasePage implements BOStoresCreatePageInterfa private readonly storeForm: string; - private readonly nameInput: string; + private readonly nameInput: (languageId: string) => string; - private readonly address1Input: string; + private readonly address1Input: (languageId: string) => string; - private readonly address2Input: string; + private readonly address2Input: (languageId: string) => string; private readonly postcodeInput: string; @@ -39,13 +39,13 @@ class BOStoresCreatePage extends BOBasePage implements BOStoresCreatePageInterfa private readonly emailInput: string; - private readonly noteTextarea: string; + private readonly noteIframe: (languageId: string) => string; private readonly statusToggle: (toggle: string) => string; private readonly pictureInput: string; - private readonly hoursInput: (pos: number, languageId: string) => string; + private readonly hoursInput: (day: number, languageId: string) => string; private readonly saveButton: string; @@ -56,33 +56,32 @@ class BOStoresCreatePage extends BOBasePage implements BOStoresCreatePageInterfa constructor() { super(); - this.pageTitleCreate = 'Stores > Add new •'; - this.pageTitleEdit = 'Stores > Edit:'; + this.pageTitleCreate = 'New store •'; + this.pageTitleEdit = 'Edit store •'; // Form selectors - this.storeForm = '#store_form'; - this.nameInput = '#name'; - this.address1Input = '#address1'; - this.address2Input = '#address2'; - this.postcodeInput = '#postcode'; - this.cityInput = '#city'; - this.countrySelect = '#id_country'; - this.stateSelect = '#id_state'; - this.latitudeInput = '#latitude'; - this.longitudeInput = '#longitude'; - this.phoneInput = '#phone'; - this.faxInput = '#fax'; - this.emailInput = '#email'; - this.noteTextarea = '#note_1'; - this.statusToggle = (toggle: string) => `${this.storeForm} #active_${toggle}`; - this.pictureInput = `${this.storeForm} #image`; - this.hoursInput = (pos: number, languageId: string) => `input[name='hours[${pos}][${languageId}]']`; - this.saveButton = '#store_form_submit_btn'; - this.alertSuccessBlockParagraph = '.alert-success'; + this.storeForm = 'form[name="store"]'; + this.nameInput = (languageId: string) => `#store_name_${languageId}`; + this.address1Input = (languageId: string) => `#store_address1_${languageId}`; + this.address2Input = (languageId: string) => `#store_address2_${languageId}`; + this.postcodeInput = '#store_postcode'; + this.cityInput = '#store_city'; + this.countrySelect = '#store_id_country'; + this.stateSelect = '#store_id_state'; + this.latitudeInput = '#store_latitude'; + this.longitudeInput = '#store_longitude'; + this.phoneInput = '#store_phone'; + this.faxInput = '#store_fax'; + this.emailInput = '#store_email'; + this.noteIframe = (languageId: string) => `#store_note_${languageId}_ifr`; + this.statusToggle = (toggle: string) => `${this.storeForm} #store_active_${toggle}`; + this.pictureInput = '#store_image'; + // Days are 0-indexed in the form (Monday = 0) + this.hoursInput = (day: number, languageId: string) => `#store_hours_${languageId}_${day - 1}`; + this.saveButton = '#save-button'; } /* Methods */ - /** * Fill creation / edition form for store and save it * @param page {Page} Browser tab @@ -90,43 +89,32 @@ class BOStoresCreatePage extends BOBasePage implements BOStoresCreatePageInterfa * @return {Promise} */ async createEditStore(page: Page, storeData: FakerStore): Promise { - // Set name - await this.setValue(page, `${this.nameInput}_1`, storeData.name); - - // Set address inputs - await this.setValue(page, `${this.address1Input}_1`, storeData.address1); - await this.setValue(page, `${this.address2Input}_1`, storeData.address2); - await this.setValue(page, this.postcodeInput, storeData.postcode); + await this.setValue(page, this.nameInput('1'), storeData.name); + await this.setValue(page, this.address1Input('1'), storeData.address1); + await this.setValue(page, this.address2Input('1'), storeData.address2); await this.setValue(page, this.cityInput, storeData.city); + // Changing the country reloads the postcode and state fields await this.selectByVisibleText(page, this.countrySelect, storeData.country); + await page.waitForLoadState('networkidle'); + await this.setValue(page, this.postcodeInput, storeData.postcode); await this.setValue(page, this.latitudeInput, storeData.latitude); await this.setValue(page, this.longitudeInput, storeData.longitude); - - // Set phone inputs await this.setValue(page, this.phoneInput, storeData.phone); await this.setValue(page, this.faxInput, storeData.fax); - - // Set email and notes inputs await this.setValue(page, this.emailInput, storeData.email); - await this.setValue(page, this.noteTextarea, storeData.note); + await this.setValueOnTinymceInput(page, this.noteIframe('1'), storeData.note); + await this.setChecked(page, this.statusToggle(storeData.status ? '1' : '0')); - // Set store status - await this.setChecked(page, this.statusToggle(storeData.status ? 'on' : 'off')); - - // Set store picture if (storeData.picture) { await this.uploadFile(page, this.pictureInput, storeData.picture); } - // Set opening hours - for (let day:number = 1; day <= 7; day++) { + for (let day: number = 1; day <= 7; day++) { await this.setValue(page, this.hoursInput(day, '1'), storeData.hours[day - 1]); } - // Save store await this.clickAndWaitForURL(page, this.saveButton); - // Return successful message return this.getAlertSuccessBlockParagraphContent(page); } @@ -136,20 +124,21 @@ class BOStoresCreatePage extends BOBasePage implements BOStoresCreatePageInterfa * @param inputName {string} * @param languageId {string | undefined} */ - async getInputValue(page: Page, inputName: string, languageId?: string): Promise { - let selector; + async getInputValue(page: Page, inputName: string, languageId: string = '1'): Promise { + const days: string[] = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']; + let selector: string; switch (inputName) { case 'name': - selector = `${this.nameInput}_${languageId}`; + selector = this.nameInput(languageId); break; case 'address1': - selector = `${this.address1Input}_${languageId}`; + selector = this.address1Input(languageId); break; case 'address2': - selector = `${this.address2Input}_${languageId}`; + selector = this.address2Input(languageId); break; case 'postcode': @@ -180,38 +169,14 @@ class BOStoresCreatePage extends BOBasePage implements BOStoresCreatePageInterfa selector = this.emailInput; break; - case 'monday': - selector = this.hoursInput(1, languageId === undefined ? '1' : languageId); - break; - - case 'tuesday': - selector = this.hoursInput(2, languageId === undefined ? '1' : languageId); - break; - - case 'wednesday': - selector = this.hoursInput(3, languageId === undefined ? '1' : languageId); - break; - - case 'thursday': - selector = this.hoursInput(4, languageId === undefined ? '1' : languageId); - break; - - case 'friday': - selector = this.hoursInput(5, languageId === undefined ? '1' : languageId); - break; - - case 'saturday': - selector = this.hoursInput(6, languageId === undefined ? '1' : languageId); - break; - - case 'sunday': - selector = this.hoursInput(7, languageId === undefined ? '1' : languageId); - break; default: - throw new Error(`Column ${inputName} was not found`); + if (!days.includes(inputName)) { + throw new Error(`Field ${inputName} was not found`); + } + selector = this.hoursInput(days.indexOf(inputName) + 1, languageId); } - return this.getAttributeContent(page, selector, 'value'); + return page.locator(selector).inputValue(); } /** @@ -239,7 +204,7 @@ class BOStoresCreatePage extends BOBasePage implements BOStoresCreatePageInterfa /** * Get Toggle value * @param page {Page} - * @param toggle {string} + * @param toggle {string} '1' for enabled, '0' for disabled */ async isActive(page: Page, toggle: string): Promise { return this.isChecked(page, this.statusToggle(toggle)); diff --git a/src/versions/develop/pages/BO/shopParameters/stores/index.ts b/src/versions/develop/pages/BO/shopParameters/stores/index.ts index ff7242ae8..dfe3ae8ba 100644 --- a/src/versions/develop/pages/BO/shopParameters/stores/index.ts +++ b/src/versions/develop/pages/BO/shopParameters/stores/index.ts @@ -15,7 +15,7 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { private readonly newStoreLink: string; - private readonly gridForm: string; + private readonly gridPanel: string; private readonly gridTableHeaderTitle: string; @@ -25,9 +25,7 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { private readonly tableHead: string; - private readonly sortColumnDiv: (column: number) => string; - - private readonly sortColumnSpanButton: (column: number) => string; + private readonly sortColumnDiv: (column: string) => string; private readonly filterRow: string; @@ -45,23 +43,7 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { private readonly tableBodyColumn: (row: number) => string; - private readonly tableColumnId: (row: number) => string; - - private readonly tableColumnName: (row: number) => string; - - private readonly tableColumnAddress: (row: number) => string; - - private readonly tableColumnCity: (row: number) => string; - - private readonly tableColumnPostalCode: (row: number) => string; - - private readonly tableColumnState: (row: number) => string; - - private readonly tableColumnCountry: (row: number) => string; - - private readonly tableColumnPhone: (row: number) => string; - - private readonly tableColumnFax: (row: number) => string; + private readonly tableColumn: (row: number, column: string) => string; private readonly tableColumnStatus: (row: number) => string; @@ -83,28 +65,24 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { private readonly bulkActionDropdownMenu: string; - private readonly selectAllLink: string; + private readonly selectAllCheckbox: string; - private readonly enableSelectionLink: string; + private readonly bulkEnableLink: string; - private readonly disableSelectionLink: string; + private readonly bulkDisableLink: string; private readonly bulkDeleteLink: string; - private readonly paginationActiveLabel: string; - private readonly paginationDiv: string; - private readonly paginationDropdownButton: string; + private readonly paginationActiveLabel: string; - private readonly paginationItems: (number: number) => string; + private readonly paginationLimitSelect: string; private readonly paginationPreviousLink: string; private readonly paginationNextLink: string; - private readonly contactDetailsForm: string; - private readonly nameInput: string; private readonly emailInput: string; @@ -136,89 +114,73 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { this.pageTitle = 'Stores •'; this.contactFormSuccessfulUpdateMessage = 'The settings have been successfully updated.'; - - this.alertSuccessBlockParagraph = '.alert-success'; + this.successfulMultiDeleteMessage = 'The selection has been successfully deleted.'; // Header selectors - this.newStoreLink = 'a[data-role=page-header-desc-store-link]'; + this.newStoreLink = '#page-header-desc-configuration-add_store'; - // Form selectors - this.gridForm = '#form-store'; - this.gridTableHeaderTitle = `${this.gridForm} .panel-heading`; - this.gridTableNumberOfTitlesSpan = `${this.gridTableHeaderTitle} span.badge`; - - // Table selectors - this.gridTable = '#table-store'; + // Grid selectors + this.gridPanel = '#store_grid_panel'; + this.gridTableHeaderTitle = `${this.gridPanel} .card-header`; + this.gridTableNumberOfTitlesSpan = `${this.gridTableHeaderTitle} .card-header-title`; + this.gridTable = '#store_grid_table'; // Sort selectors this.tableHead = `${this.gridTable} thead`; - this.sortColumnDiv = (column: number) => `${this.tableHead} th:nth-child(${column})`; - this.sortColumnSpanButton = (column: number) => `${this.sortColumnDiv(column)} span.ps-sort`; + this.sortColumnDiv = (column: string) => `${this.tableHead} div.ps-sortable-column[data-sort-col-name="${column}"]`; // Filter selectors - this.filterRow = `${this.gridTable} tr.filter`; - this.filterColumn = (filterBy: string) => `${this.filterRow} [name='storeFilter_${filterBy}']`; - this.filterSearchButton = '#submitFilterButtonstore'; - this.filterResetButton = 'button[name=\'submitResetstore\']'; + this.filterRow = `${this.gridTable} tr.column-filters`; + this.filterColumn = (filterBy: string) => `${this.filterRow} [name="store[${filterBy}]"]`; + this.filterSearchButton = `${this.filterRow} button.grid-search-button`; + this.filterResetButton = `${this.filterRow} button.grid-reset-button`; // Table body selectors this.tableBody = `${this.gridTable} tbody`; this.tableBodyRows = `${this.tableBody} tr`; this.tableBodyRow = (row: number) => `${this.tableBodyRows}:nth-child(${row})`; this.tableBodyColumn = (row: number) => `${this.tableBodyRow(row)} td`; - - // Columns selectors - this.tableColumnId = (row: number) => `${this.tableBodyColumn(row)}:nth-child(2)`; - this.tableColumnName = (row: number) => `${this.tableBodyColumn(row)}:nth-child(3)`; - this.tableColumnAddress = (row: number) => `${this.tableBodyColumn(row)}:nth-child(4)`; - this.tableColumnCity = (row: number) => `${this.tableBodyColumn(row)}:nth-child(5)`; - this.tableColumnPostalCode = (row: number) => `${this.tableBodyColumn(row)}:nth-child(6)`; - this.tableColumnState = (row: number) => `${this.tableBodyColumn(row)}:nth-child(7)`; - this.tableColumnCountry = (row: number) => `${this.tableBodyColumn(row)}:nth-child(8)`; - this.tableColumnPhone = (row: number) => `${this.tableBodyColumn(row)}:nth-child(9)`; - this.tableColumnFax = (row: number) => `${this.tableBodyColumn(row)}:nth-child(10)`; - this.tableColumnStatus = (row: number) => `${this.tableBodyColumn(row)}:nth-child(11) a`; + this.tableColumn = (row: number, column: string) => `${this.tableBodyColumn(row)}.column-${column}`; + this.tableColumnStatus = (row: number) => `${this.tableColumn(row, 'active')} .ps-switch`; // Row actions selectors - this.tableColumnActions = (row: number) => `${this.tableBodyColumn(row)} .btn-group-action`; - this.tableColumnActionsEditLink = (row: number) => `${this.tableColumnActions(row)} a.edit`; - this.tableColumnActionsToggleButton = (row: number) => `${this.tableColumnActions(row)} button.dropdown-toggle`; + this.tableColumnActions = (row: number) => `${this.tableColumn(row, 'actions')}`; + this.tableColumnActionsEditLink = (row: number) => `${this.tableColumnActions(row)} a.grid-edit-row-link`; + this.tableColumnActionsToggleButton = (row: number) => `${this.tableColumnActions(row)} a.dropdown-toggle`; this.tableColumnActionsDropdownMenu = (row: number) => `${this.tableColumnActions(row)} .dropdown-menu`; - this.tableColumnActionsDeleteLink = (row: number) => `${this.tableColumnActionsDropdownMenu(row)} a.delete`; + this.tableColumnActionsDeleteLink = (row: number) => `${this.tableColumnActionsDropdownMenu(row)} a.grid-delete-row-link`; // Confirmation modal - this.deleteModalButtonYes = '#popup_ok'; + this.deleteModalButtonYes = '#store-grid-confirm-modal button.btn-confirm-submit'; // Bulk actions selectors - this.bulkActionBlock = 'div.bulk-actions'; - this.bulkActionMenuButton = '#bulk_action_menu_store'; - this.bulkActionDropdownMenu = `${this.bulkActionBlock} ul.dropdown-menu`; - this.selectAllLink = `${this.bulkActionDropdownMenu} li:nth-child(1)`; - this.enableSelectionLink = `${this.bulkActionDropdownMenu} li:nth-child(4)`; - this.disableSelectionLink = `${this.bulkActionDropdownMenu} li:nth-child(5)`; - this.bulkDeleteLink = `${this.bulkActionDropdownMenu} li:nth-child(7)`; + this.bulkActionBlock = '#store_grid .btn-group'; + this.bulkActionMenuButton = `${this.bulkActionBlock} button.js-bulk-actions-btn`; + this.bulkActionDropdownMenu = `${this.bulkActionBlock} div.dropdown-menu.show`; + this.selectAllCheckbox = `${this.filterRow} td[data-type="bulk_action"] #store_grid_bulk_action_select_all`; + this.bulkEnableLink = `${this.bulkActionDropdownMenu} #store_grid_bulk_action_store_bulk_enable`; + this.bulkDisableLink = `${this.bulkActionDropdownMenu} #store_grid_bulk_action_store_bulk_disable`; + this.bulkDeleteLink = `${this.bulkActionDropdownMenu} #store_grid_bulk_action_delete_selection`; // Pagination selectors - this.paginationActiveLabel = `${this.gridForm} ul.pagination.pull-right li.active a`; - this.paginationDiv = `${this.gridForm} .pagination`; - this.paginationDropdownButton = `${this.paginationDiv} .dropdown-toggle`; - this.paginationItems = (number: number) => `${this.gridForm} .dropdown-menu a[data-items='${number}']`; - this.paginationPreviousLink = `${this.gridForm} .icon-angle-left`; - this.paginationNextLink = `${this.gridForm} .icon-angle-right`; + this.paginationDiv = `${this.gridPanel} .pagination-block`; + this.paginationActiveLabel = `${this.paginationDiv} ul.pagination + div .col-form-label`; + this.paginationLimitSelect = `${this.paginationDiv} #paginator_select_page_limit`; + this.paginationPreviousLink = `${this.gridPanel} a[data-role="previous-page-link"]`; + this.paginationNextLink = `${this.gridPanel} a[data-role="next-page-link"]`; // Contact details form - this.contactDetailsForm = '#store_fieldset_contact'; - this.nameInput = `${this.contactDetailsForm} input[name='PS_SHOP_NAME']`; - this.emailInput = `${this.contactDetailsForm} input[name='PS_SHOP_EMAIL']`; - this.registrationNumberTextarea = '#conf_id_PS_SHOP_DETAILS textarea[name=\'PS_SHOP_DETAILS\']'; - this.address1Input = `${this.contactDetailsForm} input[name='PS_SHOP_ADDR1']`; - this.address2Input = `${this.contactDetailsForm} input[name='PS_SHOP_ADDR2']`; - this.postcodeInput = `${this.contactDetailsForm} input[name='PS_SHOP_CODE']`; - this.cityInput = `${this.contactDetailsForm} input[name='PS_SHOP_CITY']`; - this.countrySelect = '#PS_SHOP_COUNTRY_ID'; - this.phoneInput = `${this.contactDetailsForm} input[name='PS_SHOP_PHONE']`; - this.faxInput = `${this.contactDetailsForm} input[name='PS_SHOP_FAX']`; - this.saveButton = `${this.contactDetailsForm} button[name='submitOptionsstore']`; + this.nameInput = '#stores-contact-details_name'; + this.emailInput = '#stores-contact-details_email'; + this.registrationNumberTextarea = '#stores-contact-details_registration_number'; + this.address1Input = '#stores-contact-details_address1'; + this.address2Input = '#stores-contact-details_address2'; + this.postcodeInput = '#stores-contact-details_postcode'; + this.cityInput = '#stores-contact-details_city'; + this.countrySelect = '#stores-contact-details_id_country'; + this.phoneInput = '#stores-contact-details_phone'; + this.faxInput = '#stores-contact-details_fax'; + this.saveButton = '#save-contact-details-button'; } /* Header methods */ @@ -232,7 +194,6 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { } /* Filter methods */ - /** * Get Number of stores * @param page {Page} Browser tab @@ -273,96 +234,46 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { * @return {Promise} */ async filterTable(page: Page, filterType: string, filterBy: string, value: string): Promise { - const currentUrl: string = page.url(); - switch (filterType) { case 'input': await this.setValue(page, this.filterColumn(filterBy), value.toString()); - await this.clickAndWaitForURL(page, this.filterSearchButton); break; case 'select': - await Promise.all([ - page.waitForURL((url: URL): boolean => url.toString() !== currentUrl), - this.selectByVisibleText(page, this.filterColumn(filterBy), value === '1' ? 'Yes' : 'No'), - ]); + await this.selectByVisibleText(page, this.filterColumn(filterBy), value === '1' ? 'Yes' : 'No'); break; default: throw new Error(`Filter ${filterBy} was not found`); } + + await this.clickAndWaitForURL(page, this.filterSearchButton); } /* Column methods */ - /** * Get text from column in table * @param page {Page} Browser tab * @param row {number} Row on table - * @param columnName {string} Column name of the value to return + * @param columnName {string} Column id of the value to return * @return {Promise} */ async getTextColumn(page: Page, row: number, columnName: string): Promise { - let columnSelector; - - switch (columnName) { - case 'id_store': - columnSelector = this.tableColumnId(row); - break; - - case 'sl!name': - columnSelector = this.tableColumnName(row); - break; - - case 'sl!address1': - columnSelector = this.tableColumnAddress(row); - break; - - case 'city': - columnSelector = this.tableColumnCity(row); - break; - - case 'postcode': - columnSelector = this.tableColumnPostalCode(row); - break; - - case 'st!name': - columnSelector = this.tableColumnState(row); - break; - - case 'cl!name': - columnSelector = this.tableColumnCountry(row); - break; - - case 'phone': - columnSelector = this.tableColumnPhone(row); - break; - - case 'fax': - columnSelector = this.tableColumnFax(row); - break; - - default: - throw new Error(`Column ${columnName} was not found`); - } - - return this.getTextContent(page, columnSelector); + return this.getTextContent(page, this.tableColumn(row, columnName)); } /** * Get column content from all rows * @param page {Page} Browser tab - * @param columnName {string} Column name of the value to return + * @param columnName {string} Column id of the value to return * @return {Promise>} */ async getAllRowsColumnContent(page: Page, columnName: string): Promise { - const rowsNumber = await this.getNumberOfElementInGrid(page); + const rowsNumber = await page.locator(this.tableBodyRows).count(); const allRowsContentTable: string[] = []; - // Get text column from each row for (let i = 1; i <= rowsNumber; i++) { - const rowContent = await this.getTextColumn(page, i, columnName); - allRowsContentTable.push(rowContent); + allRowsContentTable.push(await this.getTextColumn(page, i, columnName)); } return allRowsContentTable; @@ -375,7 +286,9 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { * @return {Promise} */ async getStoreStatus(page: Page, row: number): Promise { - return this.elementVisible(page, `${this.tableColumnStatus(row)}.action-enabled`, 1000); + const inputValue = await this.getAttributeContent(page, `${this.tableColumnStatus(row)} input:checked`, 'value'); + + return inputValue === '1'; } /** @@ -386,10 +299,8 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { * @return {Promise} */ async setStoreStatus(page: Page, row: number, wantedStatus: boolean): Promise { - const actualStatus = await this.getStoreStatus(page, row); - - if (actualStatus !== wantedStatus) { - await page.locator(this.tableColumnStatus(row)).click(); + if (await this.getStoreStatus(page, row) !== wantedStatus) { + await this.clickAndWaitForURL(page, this.tableColumnStatus(row)); } } @@ -417,31 +328,23 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { ]); await page.locator(this.tableColumnActionsDeleteLink(row)).click(); - - // Confirm delete action + await this.waitForVisibleSelector(page, this.deleteModalButtonYes); await this.clickAndWaitForURL(page, this.deleteModalButtonYes); - // Get successful message return this.getAlertSuccessBlockParagraphContent(page); } /* Bulk actions methods */ - /** - * Select all rows for bulk action + * Select all rows and open the bulk actions menu * @param page {Page} Browser tab * @return {Promise} */ - async selectAllRow(page: Page): Promise { - await Promise.all([ - page.locator(this.bulkActionMenuButton).click(), - this.waitForVisibleSelector(page, this.selectAllLink), - ]); - - await Promise.all([ - page.locator(this.selectAllLink).click(), - this.waitForHiddenSelector(page, this.selectAllLink), - ]); + private async openBulkActionsMenu(page: Page): Promise { + await page.locator(this.selectAllCheckbox).click(); + await page.locator(`${this.bulkActionMenuButton}:not([disabled])`).waitFor({state: 'visible'}); + await page.locator(this.bulkActionMenuButton).click(); + await this.waitForVisibleSelector(page, this.bulkActionDropdownMenu); } /** @@ -451,22 +354,8 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { * @return {Promise} */ async bulkUpdateStoreStatus(page: Page, statusWanted: boolean): Promise { - // Select all rows - await this.selectAllRow(page); - - // Perform bulk update status - await Promise.all([ - page.locator(this.bulkActionMenuButton).click(), - this.waitForVisibleSelector( - page, - this.enableSelectionLink, - ), - ]); - - await this.clickAndWaitForURL( - page, - statusWanted ? this.enableSelectionLink : this.disableSelectionLink, - ); + await this.openBulkActionsMenu(page); + await this.clickAndWaitForURL(page, statusWanted ? this.bulkEnableLink : this.bulkDisableLink); } /** @@ -475,21 +364,11 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { * @return {Promise} */ async bulkDeleteStores(page: Page): Promise { - // To confirm bulk delete action with dialog - await this.dialogListener(page, true); - - // Select all rows - await this.selectAllRow(page); - - // Perform delete - await Promise.all([ - page.locator(this.bulkActionMenuButton).click(), - this.waitForVisibleSelector(page, this.bulkDeleteLink), - ]); - - await this.clickAndWaitForURL(page, this.bulkDeleteLink); + await this.openBulkActionsMenu(page); + await page.locator(this.bulkDeleteLink).click(); + await this.waitForVisibleSelector(page, this.deleteModalButtonYes); + await this.clickAndWaitForURL(page, this.deleteModalButtonYes); - // Return successful message return this.getAlertSuccessBlockParagraphContent(page); } @@ -497,80 +376,43 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { /** * Sort table by clicking on column name * @param page {Page} Browser tab - * @param sortBy {string} Column name to sort with + * @param sortBy {string} Column id to sort with * @param sortDirection {string} Sort direction by asc or desc * @return {Promise} */ async sortTable(page: Page, sortBy: string, sortDirection: string): Promise { - let columnSelector; - - switch (sortBy) { - case 'id_store': - columnSelector = this.sortColumnDiv(2); - break; - - case 'sl!name': - columnSelector = this.sortColumnDiv(3); - break; - - case 'sl!address1': - columnSelector = this.sortColumnDiv(4); - break; - - case 'city': - columnSelector = this.sortColumnDiv(5); - break; - - case 'postcode': - columnSelector = this.sortColumnDiv(6); - break; - - case 'st!name': - columnSelector = this.sortColumnDiv(7); - break; + const sortColumnDiv = `${this.sortColumnDiv(sortBy)}[data-sort-direction="${sortDirection}"]`; - case 'cl!name': - columnSelector = this.sortColumnDiv(8); - break; - - default: - throw new Error(`Column ${sortBy} was not found`); + let i: number = 0; + while (await this.elementNotVisible(page, sortColumnDiv, 2000) && i < 2) { + await this.clickAndWaitForURL(page, this.sortColumnDiv(sortBy)); + i += 1; } - const sortColumnButton = `${columnSelector} i.icon-caret-${sortDirection}`; - await this.clickAndWaitForURL(page, sortColumnButton); + await this.waitForVisibleSelector(page, sortColumnDiv, 20000); } /* Form functions */ /** - * Se contact details + * Set contact details * @param page {Page} Browser tab * @param storeContactData {FakerStore} Store contact data to set on contact detail form * @returns {Promise} */ async setContactDetails(page: Page, storeContactData: FakerStore): Promise { - // Set name await this.setValue(page, this.nameInput, storeContactData.name); - - // Set email and registration number inputs await this.setValue(page, this.emailInput, storeContactData.email); await this.setValue(page, this.registrationNumberTextarea, storeContactData.registrationNumber); - - // Set address inputs await this.setValue(page, this.address1Input, storeContactData.address1); await this.setValue(page, this.address2Input, storeContactData.address2); await this.setValue(page, this.postcodeInput, storeContactData.postcode); await this.setValue(page, this.cityInput, storeContactData.city); await this.selectByVisibleText(page, this.countrySelect, storeContactData.country); - - // Set phone inputs await this.setValue(page, this.phoneInput, storeContactData.phone); await this.setValue(page, this.faxInput, storeContactData.fax); - // Save contact details - await page.locator(this.saveButton).click(); + await this.clickAndWaitForURL(page, this.saveButton); - // Return successful message return this.getAlertSuccessBlockParagraphContent(page); } @@ -591,8 +433,12 @@ class BOStoresPage extends BOBasePage implements BOStoresPageInterface { * @returns {Promise} */ async selectPaginationLimit(page: Page, number: number): Promise { - await this.waitForSelectorAndClick(page, this.paginationDropdownButton); - await this.clickAndWaitForURL(page, this.paginationItems(number)); + const currentUrl: string = page.url(); + + await Promise.all([ + page.waitForURL((url: URL): boolean => url.toString() !== currentUrl), + page.locator(this.paginationLimitSelect).selectOption({label: number.toString()}), + ]); return this.getPaginationLabel(page); }