Skip to content

feat: search product by search #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.
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.
1 change: 0 additions & 1 deletion src/test/features/Registration.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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!@ |
17 changes: 17 additions & 0 deletions src/test/features/ShopBySearch.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@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.

Scenario: User navigates to any random product to get its title.
When The user clicks on the "<Section>" section and the user clicks on "<Attire>" option.
And The products are shown and user navigates to a product.
And User searches that product.

Examples:
| Section | Attire |
| Men | Shorts |
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
24 changes: 24 additions & 0 deletions src/test/pages/ShopBySearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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<any>{
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');
};
};
Original file line number Diff line number Diff line change
@@ -1,46 +1,48 @@
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){
return registrationPage.webElements.find((element: PageElement) => element.elementName == resourceName) as PageElement;
}

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 = {
// public static savedProductName:string;

userShoppingByWearByWearLocators = {
shoppingSectionHeader:() => pageFixture.page.locator(getResource('shoppingSectionHeader').selectorValue),
attireSectionBtn:() => pageFixture.page.locator(getResource('attireSectionBtn').selectorValue),
itemsShown:() => pageFixture.page.locator(getResource('itemsShown').selectorValue),
Expand All @@ -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)

};

Expand All @@ -69,7 +73,7 @@ import { Page, expect } from "@playwright/test";
};

public async showItems():Promise<void>{
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();
Expand All @@ -79,39 +83,43 @@ import { Page, expect } from "@playwright/test";
};
};

public async selectRandomProduct():Promise<void>{
const getNumberOfProducts = await this.userShoppingLocators.productShown().count();
public async selectRandomProduct():Promise<any>{
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 {
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.logger.warn('User not navigated, retrying click');
await pageFixture.page.waitForLoadState('networkidle');
for(let i=0;i<listCount;i++){
await el.dblclick({force: true, timeout: 3000});
}
} else {
pageFixture.logger.info('User navigated successfully.')
await pageFixture.logger.info('User navigated successfully.')
};
};
};

public async saveSelectedProduct():Promise<any>{
const productName = (await (this.userShoppingByWearByWearLocators.pageTitle().textContent())).trim();
return productName;
};

public async getProductPriceAndSizes():Promise<void>{
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();
Expand All @@ -122,31 +130,31 @@ 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<void>{
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) {
Math.floor(Math.random() * getColorSwatch);
} 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<void>{
await this.userShoppingLocators.addToCartBtn().click();
await expect(this.userShoppingLocators.pageMessage()).toBeVisible();
await this.userShoppingByWearByWearLocators.addToCartBtn().click();
await expect(this.userShoppingByWearByWearLocators.pageMessage()).toBeVisible();
};


Expand Down
4 changes: 4 additions & 0 deletions src/test/resources/registrationPage.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
{
"elementName": "pageMessage",
"selectorValue": "//div[contains(@class,'page messages')]"
},
{
"elementName": "productTitle",
"selectorValue": "//h1[contains(@class,'page-title')]"
}
]
}
13 changes: 13 additions & 0 deletions src/test/resources/shopBySearchPage.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
10 changes: 10 additions & 0 deletions src/test/steps/shopBySearch.ts
Original file line number Diff line number Diff line change
@@ -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();
});
Original file line number Diff line number Diff line change
@@ -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);
Expand Down
Loading