|
| 1 | +import {type FoMyWishlistsViewPageInterface} from '@interfaces/FO/myAccount/myWishlists/view'; |
| 2 | +import FOBasePage from '@pages/FO/FOBasePage'; |
| 3 | + |
| 4 | +import type {Page} from 'playwright'; |
| 5 | + |
| 6 | +/** |
| 7 | + * My Wishlists View page, contains functions that can be used on the page |
| 8 | + * @class |
| 9 | + * @extends FOBasePage |
| 10 | + */ |
| 11 | +class WishlistPage extends FOBasePage implements FoMyWishlistsViewPageInterface { |
| 12 | + public readonly messageSuccessfullyRemoved: string; |
| 13 | + |
| 14 | + private readonly headerTitle: string; |
| 15 | + |
| 16 | + private readonly productList: string; |
| 17 | + |
| 18 | + private readonly productListItem: string; |
| 19 | + |
| 20 | + private readonly productListItemNth: (nth: number) => string; |
| 21 | + |
| 22 | + private readonly productListItemNthTitle: (nth: number) => string; |
| 23 | + |
| 24 | + private readonly productListItemNthCombinations: (nth: number) => string; |
| 25 | + |
| 26 | + private readonly productListItemNthUnavailable: (nth: number) => string; |
| 27 | + |
| 28 | + private readonly productListItemNthBtnAddToCart: (nth: number) => string; |
| 29 | + |
| 30 | + private readonly productListItemNthBtnDelete: (nth: number) => string; |
| 31 | + |
| 32 | + private readonly modalDelete: string; |
| 33 | + |
| 34 | + private readonly modalDeleteBtnRemove: string; |
| 35 | + |
| 36 | + private readonly toastText: string; |
| 37 | + |
| 38 | + /** |
| 39 | + * @constructs |
| 40 | + * Setting up texts and selectors to use on My Wishlists View page |
| 41 | + */ |
| 42 | + constructor(theme: string = 'classic') { |
| 43 | + super(theme); |
| 44 | + |
| 45 | + // Message |
| 46 | + this.messageSuccessfullyRemoved = 'Product successfully removed'; |
| 47 | + |
| 48 | + // Selectors |
| 49 | + this.headerTitle = '#content-wrapper h1'; |
| 50 | + this.productList = '.wishlist-products-list'; |
| 51 | + this.productListItem = `${this.productList} .wishlist-products-item`; |
| 52 | + this.productListItemNth = (nth: number) => `${this.productListItem}:nth-child(${nth})`; |
| 53 | + this.productListItemNthTitle = (nth: number) => `${this.productListItemNth(nth)} .wishlist-product-title`; |
| 54 | + this.productListItemNthCombinations = (nth: number) => `${this.productListItemNth(nth)} .wishlist-product-combinations-text`; |
| 55 | + this.productListItemNthUnavailable = (nth: number) => `${this.productListItemNth(nth)} .wishlist-product-availability`; |
| 56 | + this.productListItemNthBtnAddToCart = (nth: number) => `${this.productListItemNth(nth)} .wishlist-product-addtocart`; |
| 57 | + this.productListItemNthBtnDelete = (nth: number) => `${this.productListItemNth(nth)} .wishlist-button-add`; |
| 58 | + |
| 59 | + // Modal "Delete" |
| 60 | + this.modalDelete = '.wishlist-delete .wishlist-modal.show'; |
| 61 | + this.modalDeleteBtnRemove = `${this.modalDelete} div.modal-footer button.btn-primary`; |
| 62 | + |
| 63 | + // Toast |
| 64 | + this.toastText = '.wishlist-toast .wishlist-toast-text'; |
| 65 | + } |
| 66 | + |
| 67 | + /* |
| 68 | + Methods |
| 69 | + */ |
| 70 | + /** |
| 71 | + * @override |
| 72 | + * Get the page title from the main section |
| 73 | + * @param page {Page} Browser tab |
| 74 | + * @returns {Promise<string>} |
| 75 | + */ |
| 76 | + async getPageTitle(page: Page): Promise<string> { |
| 77 | + return this.getTextContent(page, this.headerTitle); |
| 78 | + } |
| 79 | + |
| 80 | + /** |
| 81 | + * Returns the number of product |
| 82 | + * @param page {Page} Browser tab |
| 83 | + * @returns {Promise<number>} |
| 84 | + */ |
| 85 | + async countProducts(page: Page): Promise<number> { |
| 86 | + return page.locator(this.productListItem).count(); |
| 87 | + } |
| 88 | + |
| 89 | + /** |
| 90 | + * Returns the name of a specific product |
| 91 | + * @param page {Page} Browser tab |
| 92 | + * @param nth {number} Nth of the wishlist |
| 93 | + * @returns {Promise<string>} |
| 94 | + */ |
| 95 | + async getProductName(page: Page, nth: number): Promise<string> { |
| 96 | + return this.getTextContent(page, this.productListItemNthTitle(nth)); |
| 97 | + } |
| 98 | + |
| 99 | + /** |
| 100 | + * @param page {Page} Browser tab |
| 101 | + * @param nth {number} Nth of the wishlist |
| 102 | + * @returns {Promise<string|null>} |
| 103 | + */ |
| 104 | + private async getProductCombinationValue(page: Page, nth: number, key: string): Promise<string|null> { |
| 105 | + const text = await this.getTextContent(page, this.productListItemNthCombinations(nth)); |
| 106 | + |
| 107 | + const regexResult: RegExpMatchArray[] = [...text.matchAll(/([A-Za-z]+)\s:\s([A-Za-z0-9]+)/g)]; |
| 108 | + |
| 109 | + // eslint-disable-next-line no-restricted-syntax |
| 110 | + for (const regexResultItem of regexResult) { |
| 111 | + if (regexResultItem[1].toLowerCase() === key) { |
| 112 | + return regexResultItem[2]; |
| 113 | + } |
| 114 | + } |
| 115 | + |
| 116 | + return null; |
| 117 | + } |
| 118 | + |
| 119 | + /** |
| 120 | + * Returns the quantity of a specific product |
| 121 | + * @param page {Page} Browser tab |
| 122 | + * @param nth {number} Nth of the wishlist |
| 123 | + * @returns {Promise<string>} |
| 124 | + */ |
| 125 | + async getProductQuantity(page: Page, nth: number): Promise<number> { |
| 126 | + const result: string|null = await this.getProductCombinationValue(page, nth, 'quantity'); |
| 127 | + |
| 128 | + return result ? parseInt(result, 10) : 0; |
| 129 | + } |
| 130 | + |
| 131 | + /** |
| 132 | + * Returns the value of an attribute of a specific product |
| 133 | + * @param page {Page} Browser tab |
| 134 | + * @param nth {number} Nth of the wishlist |
| 135 | + * @param attribute {string} Attribute name |
| 136 | + * @returns {Promise<string|null>} |
| 137 | + */ |
| 138 | + async getProductAttribute(page: Page, nth: number, attribute: string): Promise<string|null> { |
| 139 | + return this.getProductCombinationValue(page, nth, attribute.toLowerCase()); |
| 140 | + } |
| 141 | + |
| 142 | + /** |
| 143 | + * Returns if the product has a label Out-of-Stock |
| 144 | + * @param page {Page} Browser tab |
| 145 | + * @param nth {number} Nth of the wishlist |
| 146 | + * @returns {Promise<boolean>} |
| 147 | + */ |
| 148 | + async isProductOutOfStock(page: Page, nth: number): Promise<boolean> { |
| 149 | + return (await this.elementVisible(page, this.productListItemNthUnavailable(nth), 3000)) |
| 150 | + && (await this.getTextContent(page, this.productListItemNthUnavailable(nth)) === 'block Out-of-Stock'); |
| 151 | + } |
| 152 | + |
| 153 | + /** |
| 154 | + * Returns if the product has a label Last items in stock |
| 155 | + * @param page {Page} Browser tab |
| 156 | + * @param nth {number} Nth of the wishlist |
| 157 | + * @returns {Promise<boolean>} |
| 158 | + */ |
| 159 | + async isProductLastItemsInStock(page: Page, nth: number): Promise<boolean> { |
| 160 | + return (await this.elementVisible(page, this.productListItemNthUnavailable(nth), 3000)) |
| 161 | + && (await this.getTextContent(page, this.productListItemNthUnavailable(nth)) === 'warning Last items in stock'); |
| 162 | + } |
| 163 | + |
| 164 | + /** |
| 165 | + * Returns if the product has the button "Add to cart" disabled |
| 166 | + * @param page {Page} Browser tab |
| 167 | + * @param nth {number} Nth of the wishlist |
| 168 | + * @returns {Promise<boolean>} |
| 169 | + */ |
| 170 | + async hasButtonAddToCartDisabled(page: Page, nth: number): Promise<boolean> { |
| 171 | + return page.locator(this.productListItemNthBtnAddToCart(nth)).isDisabled(); |
| 172 | + } |
| 173 | + |
| 174 | + /** |
| 175 | + * Remove the nth Product of the wishlist |
| 176 | + * @param page {Page} Browser tab |
| 177 | + * @param nth {number} Nth of the wishlist |
| 178 | + * @returns {Promise<string>} |
| 179 | + */ |
| 180 | + async removeProduct(page: Page, nth: number): Promise<string> { |
| 181 | + await page.locator(this.productListItemNthBtnDelete(nth)).click(); |
| 182 | + |
| 183 | + // Wait for the modal |
| 184 | + await this.elementVisible(page, this.modalDelete, 3000); |
| 185 | + // Click on the first wishlist |
| 186 | + await page.locator(this.modalDeleteBtnRemove).click(); |
| 187 | + // Wait for the toast |
| 188 | + await this.elementVisible(page, this.toastText, 3000); |
| 189 | + |
| 190 | + return this.getTextContent(page, this.toastText); |
| 191 | + } |
| 192 | +} |
| 193 | + |
| 194 | +module.exports = new WishlistPage(); |
0 commit comments