diff --git a/src/interfaces/BO/catalog/products/create/tabCombinations.ts b/src/interfaces/BO/catalog/products/create/tabCombinations.ts index be11c2c37..1d54f11e4 100644 --- a/src/interfaces/BO/catalog/products/create/tabCombinations.ts +++ b/src/interfaces/BO/catalog/products/create/tabCombinations.ts @@ -52,6 +52,7 @@ export interface BOProductsCreateTabCombinationsPageInterface extends BOBasePage reference: string, }>; getCombinationNameFromModal(page: Page): Promise; + getCombinationVirtualProductFileName(page: Page): Promise; getFilterBySizeButtonName(page: Page): Promise; getNumberOfCombinationsFromList(page: Page): Promise; getPaginationLabel(page: Page): Promise; @@ -67,6 +68,8 @@ export interface BOProductsCreateTabCombinationsPageInterface extends BOBasePage setLabelWhenInStock(page: Page, label: string): Promise; setLabelWhenOutOfStock(page: Page, label: string): Promise; setOptionWhenOutOfStock(page: Page, option: string): Promise; + setCombinationIsVirtual(page: Page, isVirtual: boolean): Promise; + setCombinationVirtualProductFile(page: Page, filePath: string): Promise; setProductAttributes(page: Page, attributes: ProductAttributes[]): Promise; sortTable(page: Page, sortBy: string, column: number, sortDirection: string): Promise; } diff --git a/src/interfaces/FO/product/index.ts b/src/interfaces/FO/product/index.ts index b7b38402c..90a8a12bf 100644 --- a/src/interfaces/FO/product/index.ts +++ b/src/interfaces/FO/product/index.ts @@ -45,6 +45,7 @@ export interface FoProductPageInterface extends FOBasePagePageInterface { getProductAttributes(page: Page): Promise; getProductAvailabilityLabel(page: Page): Promise; getProductCondition(page: Page): Promise; + getProductDownloadFileName(page: Page): Promise; getProductFeaturesList(page: Page): Promise; getProductImageUrls(page: Page): Promise; getProductInPackList(page: Page, productInList: number): Promise; diff --git a/src/versions/develop/pages/BO/catalog/products/create/tabCombinations.ts b/src/versions/develop/pages/BO/catalog/products/create/tabCombinations.ts index 877592805..fad065aff 100644 --- a/src/versions/develop/pages/BO/catalog/products/create/tabCombinations.ts +++ b/src/versions/develop/pages/BO/catalog/products/create/tabCombinations.ts @@ -170,6 +170,14 @@ class CombinationsTab extends BOBasePage implements BOProductsCreateTabCombinati private readonly editCombinationModalSaveButton: string; + private readonly editCombinationModalVirtualFileToggle: (toCheck: number) => string; + + private readonly editCombinationModalVirtualFile: string; + + private readonly editCombinationModalVirtualFileName: string; + + private readonly editCombinationModalIsVirtualToggle: (toCheck: number) => string; + private readonly editCombinationModalCloseButton: string; private readonly editCombinationCloseModal: string; @@ -329,6 +337,16 @@ class CombinationsTab extends BOBasePage implements BOProductsCreateTabCombinati this.editCombinationModalFinalPriceTaxIncludedInput = '#combination_form_price_impact_final_price_tax_included'; this.editCombinationModalLocationInput = '#combination_form_stock_options_stock_location'; this.editCombinationModalSaveButton = `${this.editCombinationModal} footer button.btn-primary`; + // Virtual product file section, shown in the combination modal only for the virtual_combinations product type. + // Ids derive deterministically from the core form: root form[name="combination_form"] + embedded + // VirtualProductFileType child `virtual_product_file` (SwitchType `has_file` -> `_has_file_1`). + this.editCombinationModalVirtualFileToggle = (toCheck: number) => ( + `#combination_form_virtual_product_file_has_file_${toCheck}` + ); + this.editCombinationModalVirtualFile = '#combination_form_virtual_product_file_file'; + this.editCombinationModalVirtualFileName = '#combination_form_virtual_product_file_name'; + // Per-combination "Is virtual" switch (SwitchType renders Yes/No radios _1/_0). + this.editCombinationModalIsVirtualToggle = (toCheck: number) => `#combination_form_is_virtual_${toCheck}`; this.editCombinationModalCloseButton = `${this.editCombinationModal} footer button.btn-close`; this.editCombinationCloseModal = `${this.editCombinationEditModal} div.modal-prevent-close div.modal.show`; this.editCombinationModalDiscardButton = `${this.editCombinationCloseModal} button.btn-primary`; @@ -654,6 +672,54 @@ class CombinationsTab extends BOBasePage implements BOProductsCreateTabCombinati return this.getAlertSuccessBlockParagraphContent(combinationFrame!); } + /** + * Upload a downloadable file to the currently open combination (virtual_combinations product type) + * @param page {Page} Browser tab + * @param filePath {string} Path of the file to upload + * @returns {Promise} + */ + async setCombinationVirtualProductFile(page: Page, filePath: string): Promise { + await this.waitForVisibleSelector(page, this.editCombinationIframe); + + const combinationFrame: Frame|null = page.frame({url: /sell\/catalog\/products\/combinations/gmi}); + + await this.setChecked(combinationFrame!, this.editCombinationModalVirtualFileToggle(1)); + await this.waitForVisibleSelector(combinationFrame!, this.editCombinationModalVirtualFile); + await this.uploadFile(combinationFrame!, this.editCombinationModalVirtualFile, filePath); + await this.setValue(combinationFrame!, this.editCombinationModalVirtualFileName, filePath); + await this.waitForSelectorAndClick(page, this.editCombinationModalSaveButton); + + return this.getAlertSuccessBlockParagraphContent(combinationFrame!); + } + + /** + * Mark the currently open combination as virtual (or physical) and save + * @param page {Page} Browser tab + * @param isVirtual {boolean} Whether the combination is virtual (non-shippable) + * @returns {Promise} + */ + async setCombinationIsVirtual(page: Page, isVirtual: boolean): Promise { + await this.waitForVisibleSelector(page, this.editCombinationIframe); + + const combinationFrame: Frame|null = page.frame({url: /sell\/catalog\/products\/combinations/gmi}); + + await this.setChecked(combinationFrame!, this.editCombinationModalIsVirtualToggle(isVirtual ? 1 : 0)); + await this.waitForSelectorAndClick(page, this.editCombinationModalSaveButton); + + return this.getAlertSuccessBlockParagraphContent(combinationFrame!); + } + + /** + * Get the name of the downloadable file attached to the currently open combination + * @param page {Page} Browser tab + * @returns {Promise} + */ + async getCombinationVirtualProductFileName(page: Page): Promise { + const combinationFrame: Frame|null = page.frame({url: /sell\/catalog\/products\/combinations/gmi}); + + return combinationFrame!.locator(this.editCombinationModalVirtualFileName).inputValue(); + } + /** * Get recent stock movements * @param page {Page} Browser tab diff --git a/src/versions/develop/pages/FO/classic/product/index.ts b/src/versions/develop/pages/FO/classic/product/index.ts index d30d75f2d..ec02384c6 100644 --- a/src/versions/develop/pages/FO/classic/product/index.ts +++ b/src/versions/develop/pages/FO/classic/product/index.ts @@ -78,6 +78,8 @@ class ProductPage extends FOBasePage implements FoProductPageInterface { protected productCondition: string; + protected productDownloadFileName: string; + protected continueShoppingButton: string; protected productAvailability: string; @@ -256,6 +258,9 @@ class ProductPage extends FOBasePage implements FoProductPageInterface { this.productDetail = 'div.product-information a[href=\'#product-details\']'; this.productFeaturesList = '#product-details section.product-features'; this.productCondition = '#product-details div.product-condition'; + // Downloadable file offered for the currently selected combination (virtual_combinations product type). + // TODO(selector): the FO theme markup is not in this checkout; confirm the real per-combination download selector. + this.productDownloadFileName = '#product-details .product-download a'; this.continueShoppingButton = `${this.blockCartModal} div.cart-content-btn button`; this.productAvailability = '#product-availability'; this.productAvailabilityIcon = `${this.productAvailability} i`; @@ -1100,6 +1105,15 @@ class ProductPage extends FOBasePage implements FoProductPageInterface { return this.getTextContent(page, this.productCondition); } + /** + * Get the downloadable file name offered for the currently selected combination + * @param page {Page} Browser tab + * @returns {Promise} + */ + async getProductDownloadFileName(page: Page): Promise { + return this.getTextContent(page, this.productDownloadFileName); + } + /** * Is customization block visible * @param page {Page} Browser tab