From c16785e4583d3fcc8fee03a4c49d57c341fd4ca4 Mon Sep 17 00:00:00 2001 From: najeeb1023 Date: Sun, 23 Jun 2024 22:49:22 +0200 Subject: [PATCH 1/3] feat: search product by search --- README.md | 15 ++--- src/test/features/ShopBySearch.feature | 0 ...ing.feature => UserShoppingByWear.feature} | 4 +- ...{UserShopping.ts => UserShoppingByWear.ts} | 56 +++++++++---------- ...gPage.json => userShoppingPageByWear.json} | 0 ...{userShopping.ts => userShoppingByWear.ts} | 6 +- 6 files changed, 41 insertions(+), 40 deletions(-) create mode 100644 src/test/features/ShopBySearch.feature rename src/test/features/{UserShopping.feature => UserShoppingByWear.feature} (89%) rename src/test/pages/{UserShopping.ts => UserShoppingByWear.ts} (70%) rename src/test/resources/{userShoppingPage.json => userShoppingPageByWear.json} (100%) rename src/test/steps/{userShopping.ts => userShoppingByWear.ts} (78%) diff --git a/README.md b/README.md index 546d304..1f1a386 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,8 @@ ## Overview -This repository contains a test automation framework for the Luma web application using Playwright and Cucumber. The framework is designed with Page Object Model (POM) and Facade design patterns to ensure maintainability and scalability. +This repository contains a test automation framework for the Luma web application using Playwright and Cucumber. +The framework is designed with Page Object Model (POM) and Facade design patterns to ensure maintainability and scalability. ## Features @@ -34,13 +35,13 @@ This repository contains a test automation framework for the Luma web applicatio ### Installation -1. Clone the repository: +1. Clone the repository: git clone https://github.com/najeeb1023/magento-webapp.git -2. Install dependencies: +2. Install dependencies: npm i -3. To run the project: - cucumber:luma - To run all the test scenarios. - cucumber:luma:tags - Add your scenario tag in the end using -> @tag. - cucumber:luma:debug - After attatching the debugger you can easily debug the desired scenario. \ No newline at end of file +3. To run the project: + cucumber:luma - To run all the test scenarios. + cucumber:luma:tags - Add your scenario tag in the end using -> @tag. + cucumber:luma:debug - After attatching the debugger you can easily debug the desired scenario. \ No newline at end of file diff --git a/src/test/features/ShopBySearch.feature b/src/test/features/ShopBySearch.feature new file mode 100644 index 0000000..e69de29 diff --git a/src/test/features/UserShopping.feature b/src/test/features/UserShoppingByWear.feature similarity index 89% rename from src/test/features/UserShopping.feature rename to src/test/features/UserShoppingByWear.feature index 4f3eaca..242d6ad 100644 --- a/src/test/features/UserShopping.feature +++ b/src/test/features/UserShoppingByWear.feature @@ -14,8 +14,8 @@ Feature: Verify that the user is able to purchase some item. And The details of the product are shown. Examples: - | Section | Attire | - | Men | Hoodies & Sweatshirts | + | Section | Attire | + | Men | Shorts | @WomenShopping Scenario: User shops for Women Jackets. diff --git a/src/test/pages/UserShopping.ts b/src/test/pages/UserShoppingByWear.ts similarity index 70% rename from src/test/pages/UserShopping.ts rename to src/test/pages/UserShoppingByWear.ts index c6bb7a4..d417363 100644 --- a/src/test/pages/UserShopping.ts +++ b/src/test/pages/UserShoppingByWear.ts @@ -1,11 +1,11 @@ import { pageFixture } from "../hooks/pageFixture"; -import * as userShoppingPage from "../resources/userShoppingPage.json"; +import * as userShoppingByWearPage from "../resources/userShoppingPageByWear.json"; import * as registrationPage from "../resources/registrationPage.json"; import { PageElement } from "../resources/interfaces/iPageElement"; import { Page, expect } from "@playwright/test"; function getResource(resourceName: string){ - return userShoppingPage.webElements.find((element: PageElement) => element.elementName == resourceName) as PageElement; + return userShoppingByWearPage.webElements.find((element: PageElement) => element.elementName == resourceName) as PageElement; } function getResourceRegisterPage(resourceName: string){ @@ -13,34 +13,34 @@ import { Page, expect } from "@playwright/test"; } export class CategoryAndProductSelectionFacade{ - private userShopping: UserShopping; + private userShoppingByWear: UserShoppingByWear; - constructor(userShopping: UserShopping){ - this.userShopping = userShopping; + constructor(userShoppingByWear: UserShoppingByWear){ + this.userShoppingByWear = userShoppingByWear; }; public async productSelection(section: string, attire: string){ - await this.userShopping.goSectionAndAttire(section, attire); + await this.userShoppingByWear.goSectionAndAttire(section, attire); }; public async selectRandomItem(){ - await this.userShopping.showItems(); - await this.userShopping.selectRandomProduct(); + await this.userShoppingByWear.showItems(); + await this.userShoppingByWear.selectRandomProduct(); }; public async showProductDetails(){ - await this.userShopping.getProductPriceAndSizes(); - await this.userShopping.selectAndGetProductColors(); - await this.userShopping.addToCartProduct(); + await this.userShoppingByWear.getProductPriceAndSizes(); + await this.userShoppingByWear.selectAndGetProductColors(); + await this.userShoppingByWear.addToCartProduct(); }; }; - export class UserShopping { + export class UserShoppingByWear { constructor(public page: Page){ pageFixture.page = page; }; - userShoppingLocators = { + userShoppingByWearByWearLocators = { shoppingSectionHeader:() => pageFixture.page.locator(getResource('shoppingSectionHeader').selectorValue), attireSectionBtn:() => pageFixture.page.locator(getResource('attireSectionBtn').selectorValue), itemsShown:() => pageFixture.page.locator(getResource('itemsShown').selectorValue), @@ -69,7 +69,7 @@ import { Page, expect } from "@playwright/test"; }; public async showItems():Promise{ - const getNumberOfProducts = await this.userShoppingLocators.productShown().count(); + const getNumberOfProducts = await this.userShoppingByWearByWearLocators.productShown().count(); console.log(' Products shown -> ' + getNumberOfProducts + '\n'); for(let i=1;i<=getNumberOfProducts;i++){ const getEl = await pageFixture.page.locator(getResource('itemsShown').selectorValue.replace('FLAG', i.toString())).allTextContents(); @@ -80,7 +80,7 @@ import { Page, expect } from "@playwright/test"; }; public async selectRandomProduct():Promise{ - const getNumberOfProducts = await this.userShoppingLocators.productShown().count(); + const getNumberOfProducts = await this.userShoppingByWearByWearLocators.productShown().count(); let ind: number = Math.floor(Math.random() * (getNumberOfProducts - 1))+ 1; if (ind == 0) { @@ -89,8 +89,8 @@ import { Page, expect } from "@playwright/test"; const el = (pageFixture.page.locator(getResource('itemsShown').selectorValue.replace('FLAG', `${ind}`))); await expect(el).toBeVisible(); await el.dblclick({force: true, timeout: 3000}); - const list = await this.userShoppingLocators.shoppingList().isVisible(); - const listCount = await this.userShoppingLocators.shoppingList().count(); + const list = await this.userShoppingByWearByWearLocators.shoppingList().isVisible(); + const listCount = await this.userShoppingByWearByWearLocators.shoppingList().count(); if (list == true){ pageFixture.logger.warn('User not navigated, retrying click'); await pageFixture.page.waitForLoadState('networkidle'); @@ -104,14 +104,14 @@ import { Page, expect } from "@playwright/test"; }; public async getProductPriceAndSizes():Promise{ - const priceText = (await this.userShoppingLocators.productPrice().textContent()).trim(); - if(await this.userShoppingLocators.productPrice().isVisible() == true){ + const priceText = (await this.userShoppingByWearByWearLocators.productPrice().textContent()).trim(); + if(await this.userShoppingByWearByWearLocators.productPrice().isVisible() == true){ pageFixture.logger.warn('Product price is not visible, attempting to click product again.') const regEx = /\$\d+\.\d{2}/; const matchPriceText = priceText.match(regEx); console.log("The price of the product -> "+matchPriceText[0]); - const sizeText = (await this.userShoppingLocators.productSize().textContent()).trim(); - const getSizes = await this.userShoppingLocators.getProductSizesAvailable().count(); + const sizeText = (await this.userShoppingByWearByWearLocators.productSize().textContent()).trim(); + const getSizes = await this.userShoppingByWearByWearLocators.getProductSizesAvailable().count(); console.log('\x1b[36m%s\x1b[0m',sizeText+'s' + ' available are: '); for(let i=1;i<=getSizes;i++){ const el = await pageFixture.page.locator(getResource('getProductSize').selectorValue.replace('FLAG', i.toString())).allTextContents(); @@ -122,15 +122,15 @@ import { Page, expect } from "@playwright/test"; } else { return this.selectRandomProduct(); }; - const getSizes = await this.userShoppingLocators.getProductSizesAvailable().count(); + const getSizes = await this.userShoppingByWearByWearLocators.getProductSizesAvailable().count(); let ind: number = Math.floor(Math.random() * (getSizes - 1))+ 1; const sizeEl = (pageFixture.page.locator(getResource('getProductSize').selectorValue.replace('FLAG', `${ind}`))); await sizeEl.click(); - console.log("Size selected: "+await this.userShoppingLocators.getSelectedProductSize().textContent()); + console.log("Size selected: "+await this.userShoppingByWearByWearLocators.getSelectedProductSize().textContent()); }; public async selectAndGetProductColors():Promise{ - const getColorSwatch = await this.userShoppingLocators.getColorSwatches().count(); + const getColorSwatch = await this.userShoppingByWearByWearLocators.getColorSwatches().count(); console.log('Color found: ' + getColorSwatch); let ind: number = Math.floor(Math.random() * (getColorSwatch - 1)) + 1; if (ind == 0) { @@ -138,15 +138,15 @@ import { Page, expect } from "@playwright/test"; } else { const colorToBeSelect = await pageFixture.page.locator(getResource('colorSwatch').selectorValue.replace('FLAG', `${ind}`)); await colorToBeSelect.click(); - await this.userShoppingLocators.getCurrentSelectedColor().isVisible(); - const getColor = await this.userShoppingLocators.getCurrentSelectedColor().textContent(); + await this.userShoppingByWearByWearLocators.getCurrentSelectedColor().isVisible(); + const getColor = await this.userShoppingByWearByWearLocators.getCurrentSelectedColor().textContent(); console.log('Selected color: ' +getColor); }; }; public async addToCartProduct():Promise{ - await this.userShoppingLocators.addToCartBtn().click(); - await expect(this.userShoppingLocators.pageMessage()).toBeVisible(); + await this.userShoppingByWearByWearLocators.addToCartBtn().click(); + await expect(this.userShoppingByWearByWearLocators.pageMessage()).toBeVisible(); }; diff --git a/src/test/resources/userShoppingPage.json b/src/test/resources/userShoppingPageByWear.json similarity index 100% rename from src/test/resources/userShoppingPage.json rename to src/test/resources/userShoppingPageByWear.json diff --git a/src/test/steps/userShopping.ts b/src/test/steps/userShoppingByWear.ts similarity index 78% rename from src/test/steps/userShopping.ts rename to src/test/steps/userShoppingByWear.ts index 1937733..7c74a06 100644 --- a/src/test/steps/userShopping.ts +++ b/src/test/steps/userShoppingByWear.ts @@ -1,10 +1,10 @@ import { When, Then, setDefaultTimeout } from "@cucumber/cucumber"; -import { CategoryAndProductSelectionFacade, UserShopping } from "../pages/UserShopping"; +import { CategoryAndProductSelectionFacade, UserShoppingByWear } from "../pages/UserShoppingByWear"; import { pageFixture } from "../hooks/pageFixture"; setDefaultTimeout(60000); -let userShopping = new UserShopping(pageFixture.page); -let categoryAndProductSectionFacade = new CategoryAndProductSelectionFacade(userShopping); +let userShoppingByWear = new UserShoppingByWear(pageFixture.page); +let categoryAndProductSectionFacade = new CategoryAndProductSelectionFacade(userShoppingByWear); When("The user clicks on the {string} section and the user clicks on {string} option.", async function (section: string, attire: string){ await categoryAndProductSectionFacade.productSelection(section, attire); From f0c8814be3f5e5eeabff1c7c8cfa7ac280034b65 Mon Sep 17 00:00:00 2001 From: najeeb1023 Date: Mon, 24 Jun 2024 16:19:23 +0200 Subject: [PATCH 2/3] exported product name to another function to be used by search --- src/test/features/ShopBySearch.feature | 18 +++++++++++++ src/test/pages/ShopBySearch.ts | 33 ++++++++++++++++++++++++ src/test/pages/UserShoppingByWear.ts | 13 +++++++--- src/test/resources/registrationPage.json | 4 +++ src/test/resources/shopBySearchPage.json | 13 ++++++++++ src/test/steps/shopBySearch.ts | 10 +++++++ 6 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 src/test/pages/ShopBySearch.ts create mode 100644 src/test/resources/shopBySearchPage.json create mode 100644 src/test/steps/shopBySearch.ts diff --git a/src/test/features/ShopBySearch.feature b/src/test/features/ShopBySearch.feature index e69de29..e205d6b 100644 --- a/src/test/features/ShopBySearch.feature +++ b/src/test/features/ShopBySearch.feature @@ -0,0 +1,18 @@ +@ShopyBySearch + +Feature: Verify that the user is able to purchase an item by searching. + + User wants to buy something via search. + + Background: User is landed on the webpage. + Given The user lands at the webpage. + + @MenShopping + Scenario: User navigates to any random product to get its title. + When The user clicks on the "
" section and the user clicks on "" option. + And The products are shown and user navigates to a product. + And User searches that product. + + Examples: + | Section | Attire | + | Men | Shorts | \ No newline at end of file diff --git a/src/test/pages/ShopBySearch.ts b/src/test/pages/ShopBySearch.ts new file mode 100644 index 0000000..2b81e89 --- /dev/null +++ b/src/test/pages/ShopBySearch.ts @@ -0,0 +1,33 @@ +import { UserShoppingByWear } from "./UserShoppingByWear"; +import * as shopBySearchPage from "../../../src/test/resources/shopBySearchPage.json"; +import { PageElement } from "../resources/interfaces/iPageElement"; +import { pageFixture } from "../hooks/pageFixture"; + + function getResource(resourceName: string){ + return shopBySearchPage.webElements.find((elementName: PageElement) => elementName.elementName == resourceName) as PageElement; + }; + + export class ShopBySearch extends UserShoppingByWear { + + shopBySearchLocators = { + lumaLogo:() => pageFixture.page.locator(getResource('lumaLogo').selectorValue), + searchBar:() => pageFixture.page.locator(getResource('searchBar').selectorValue) + }; + + + public async searchProduct():Promise{ + // try { + const product = await this.selectRandomProduct(); // Ensure to await selectRandomProduct + //console.log('Selected product:', await product); // Debugging line + // if (product) { + const searchProduct = await product.toString(); + await this.shopBySearchLocators.searchBar().fill(await searchProduct); // Fill searchBarInput with the actual product name + await pageFixture.page.keyboard.press('Enter'); + // } else { + // console.error('No product was selected'); + }; + // } catch (error) { + // console.error('An error occurred:', error); + // } + }; +// }; \ No newline at end of file diff --git a/src/test/pages/UserShoppingByWear.ts b/src/test/pages/UserShoppingByWear.ts index d417363..ebbca72 100644 --- a/src/test/pages/UserShoppingByWear.ts +++ b/src/test/pages/UserShoppingByWear.ts @@ -40,6 +40,8 @@ import { Page, expect } from "@playwright/test"; pageFixture.page = page; }; + // public static savedProductName:string; + userShoppingByWearByWearLocators = { shoppingSectionHeader:() => pageFixture.page.locator(getResource('shoppingSectionHeader').selectorValue), attireSectionBtn:() => pageFixture.page.locator(getResource('attireSectionBtn').selectorValue), @@ -55,7 +57,9 @@ import { Page, expect } from "@playwright/test"; getColorSwatches:() => pageFixture.page.locator(getResource('getColorSwatches').selectorValue), getSelectedProductSize:() => pageFixture.page.locator(getResource('getSelectedProductSize').selectorValue), pageMessage:() => pageFixture.page.locator(getResourceRegisterPage('pageMessage').selectorValue), - addToCartBtn:() => pageFixture.page.locator(getResource('addToCartBtn').selectorValue) + addToCartBtn:() => pageFixture.page.locator(getResource('addToCartBtn').selectorValue), + pageTitle:() => pageFixture.page.locator(getResourceRegisterPage('createAccHeading').selectorValue), + productTitle:() => pageFixture.page.locator(getResourceRegisterPage('productTitle').selectorValue) }; @@ -79,10 +83,9 @@ import { Page, expect } from "@playwright/test"; }; }; - public async selectRandomProduct():Promise{ + public async selectRandomProduct():Promise{ const getNumberOfProducts = await this.userShoppingByWearByWearLocators.productShown().count(); let ind: number = Math.floor(Math.random() * (getNumberOfProducts - 1))+ 1; - if (ind == 0) { Math.floor(Math.random() * getNumberOfProducts); } else { @@ -99,8 +102,12 @@ import { Page, expect } from "@playwright/test"; } } else { pageFixture.logger.info('User navigated successfully.') + //const productName = [(await (this.userShoppingByWearByWearLocators.pageTitle().textContent())).trim()]; + // console.log(productName); }; }; + const productName = (await (this.userShoppingByWearByWearLocators.productTitle().textContent())).trim(); + return productName; }; public async getProductPriceAndSizes():Promise{ diff --git a/src/test/resources/registrationPage.json b/src/test/resources/registrationPage.json index 2017a93..8dc14e7 100644 --- a/src/test/resources/registrationPage.json +++ b/src/test/resources/registrationPage.json @@ -36,6 +36,10 @@ { "elementName": "pageMessage", "selectorValue": "//div[contains(@class,'page messages')]" + }, + { + "elementName": "productTitle", + "selectorValue": "//h1[contains(@class,'page-title')]" } ] } \ No newline at end of file diff --git a/src/test/resources/shopBySearchPage.json b/src/test/resources/shopBySearchPage.json new file mode 100644 index 0000000..f762236 --- /dev/null +++ b/src/test/resources/shopBySearchPage.json @@ -0,0 +1,13 @@ +{ + "name": "Search Locator", + "webElements": [ + { + "elementName": "lumaLogo", + "selectorValue": "//div[contains(@class,'header content')]//a[contains(@class,'logo')]" + }, + { + "elementName": "searchBar", + "selectorValue": "//div[contains(@class,'field search')]//input" + } + ] +} diff --git a/src/test/steps/shopBySearch.ts b/src/test/steps/shopBySearch.ts new file mode 100644 index 0000000..5e45c26 --- /dev/null +++ b/src/test/steps/shopBySearch.ts @@ -0,0 +1,10 @@ +import { When, setDefaultTimeout } from "@cucumber/cucumber"; +import { ShopBySearch } from "../pages/ShopBySearch"; +import { pageFixture } from "../hooks/pageFixture"; + +setDefaultTimeout(60000); +let shopBySearch = new ShopBySearch(pageFixture.page); + +When('User searches that product.', async function (){ + await shopBySearch.searchProduct(); +}); \ No newline at end of file From 4b1407fa940cb54076b867d180dccd560463d6c1 Mon Sep 17 00:00:00 2001 From: najeeb1023 Date: Mon, 24 Jun 2024 19:05:19 +0200 Subject: [PATCH 3/3] added new function to store previously found product to search it. --- README.md | 6 +++--- src/test/features/Registration.feature | 1 - src/test/features/ShopBySearch.feature | 1 - src/test/pages/ShopBySearch.ts | 21 ++++++--------------- src/test/pages/UserShoppingByWear.ts | 11 ++++++----- 5 files changed, 15 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 1f1a386..aeac3fe 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,6 @@ The framework is designed with Page Object Model (POM) and Facade design pattern npm i 3. To run the project: - cucumber:luma - To run all the test scenarios. - cucumber:luma:tags - Add your scenario tag in the end using -> @tag. - cucumber:luma:debug - After attatching the debugger you can easily debug the desired scenario. \ No newline at end of file + * cucumber:luma - To run all the test scenarios. + * cucumber:luma:tags - Add your scenario tag in the end using -> @tag. + * cucumber:luma:debug - After attatching the debugger you can easily debug the desired scenario. \ No newline at end of file diff --git a/src/test/features/Registration.feature b/src/test/features/Registration.feature index 451d5b4..a1d065a 100644 --- a/src/test/features/Registration.feature +++ b/src/test/features/Registration.feature @@ -14,5 +14,4 @@ Feature: Verify that the user is able to register an account. Examples: | FirstName | LastName | Email | Password | - | TestZulfi | ZulfiTest | zulfiTest@gmail.com | testsss123! | | FacadeUser | UserFace | facade23@gmail.com | facading123!@ | diff --git a/src/test/features/ShopBySearch.feature b/src/test/features/ShopBySearch.feature index e205d6b..db617bb 100644 --- a/src/test/features/ShopBySearch.feature +++ b/src/test/features/ShopBySearch.feature @@ -7,7 +7,6 @@ Feature: Verify that the user is able to purchase an item by searching. Background: User is landed on the webpage. Given The user lands at the webpage. - @MenShopping Scenario: User navigates to any random product to get its title. When The user clicks on the "
" section and the user clicks on "" option. And The products are shown and user navigates to a product. diff --git a/src/test/pages/ShopBySearch.ts b/src/test/pages/ShopBySearch.ts index 2b81e89..9910027 100644 --- a/src/test/pages/ShopBySearch.ts +++ b/src/test/pages/ShopBySearch.ts @@ -15,19 +15,10 @@ import { pageFixture } from "../hooks/pageFixture"; }; - public async searchProduct():Promise{ - // try { - const product = await this.selectRandomProduct(); // Ensure to await selectRandomProduct - //console.log('Selected product:', await product); // Debugging line - // if (product) { - const searchProduct = await product.toString(); - await this.shopBySearchLocators.searchBar().fill(await searchProduct); // Fill searchBarInput with the actual product name - await pageFixture.page.keyboard.press('Enter'); - // } else { - // console.error('No product was selected'); + public async searchProduct():Promise{ + const product: string = await this.saveSelectedProduct(); + console.log('Selected product:', product); + await this.shopBySearchLocators.searchBar().fill(product); // Fill searchBarInput with the actual product name + await pageFixture.page.keyboard.press('Enter'); }; - // } catch (error) { - // console.error('An error occurred:', error); - // } - }; -// }; \ No newline at end of file + }; \ No newline at end of file diff --git a/src/test/pages/UserShoppingByWear.ts b/src/test/pages/UserShoppingByWear.ts index ebbca72..f06c209 100644 --- a/src/test/pages/UserShoppingByWear.ts +++ b/src/test/pages/UserShoppingByWear.ts @@ -95,18 +95,19 @@ import { Page, expect } from "@playwright/test"; const list = await this.userShoppingByWearByWearLocators.shoppingList().isVisible(); const listCount = await this.userShoppingByWearByWearLocators.shoppingList().count(); if (list == true){ - pageFixture.logger.warn('User not navigated, retrying click'); + await pageFixture.logger.warn('User not navigated, retrying click'); await pageFixture.page.waitForLoadState('networkidle'); for(let i=0;i{ + const productName = (await (this.userShoppingByWearByWearLocators.pageTitle().textContent())).trim(); return productName; };