From b979005bd2b75212c68d0241b991f4ccb79a466b Mon Sep 17 00:00:00 2001 From: sangeeta1239 <48852123+sangeeta1239@users.noreply.github.com> Date: Fri, 20 Mar 2026 16:06:57 +0530 Subject: [PATCH] Add files via upload --- common.steps.ts | 6 ++++ login.feature | 13 +++++++++ login.page.ts | 26 +++++++++++++++++ login.steps.ts | 15 ++++++++++ product.feature | 14 +++++++++ product.page.ts | 21 ++++++++++++++ product.steps.ts | 7 +++++ purchase.feature | 14 +++++++++ purchase.page.ts | 72 +++++++++++++++++++++++++++++++++++++++++++++++ purchase.steps.ts | 64 +++++++++++++++++++++++++++++++++++++++++ 10 files changed, 252 insertions(+) create mode 100644 common.steps.ts create mode 100644 login.feature create mode 100644 login.page.ts create mode 100644 login.steps.ts create mode 100644 product.feature create mode 100644 product.page.ts create mode 100644 product.steps.ts create mode 100644 purchase.feature create mode 100644 purchase.page.ts create mode 100644 purchase.steps.ts diff --git a/common.steps.ts b/common.steps.ts new file mode 100644 index 0000000..459b742 --- /dev/null +++ b/common.steps.ts @@ -0,0 +1,6 @@ +import { Given } from "@cucumber/cucumber"; +import { getPage } from "../playwrightUtilities"; + +Given('I open the {string} page', async (url) => { + await getPage().goto(url); + }); \ No newline at end of file diff --git a/login.feature b/login.feature new file mode 100644 index 0000000..88ae473 --- /dev/null +++ b/login.feature @@ -0,0 +1,13 @@ +Feature: Login Feature + + Background: + Given I open the "https://www.saucedemo.com/" page + + Scenario: Validate the login page title + Then I will login as 'standard_user' + Then I should see the title "Labs Swag" + + Scenario: Validate login error message + And I login as 'locked_out_user' + Then I should see Login error message + \ No newline at end of file diff --git a/login.page.ts b/login.page.ts new file mode 100644 index 0000000..7efeff4 --- /dev/null +++ b/login.page.ts @@ -0,0 +1,26 @@ +import { Page } from "@playwright/test" + +export class Login { + private readonly page: Page + private readonly password: string = 'secret_sauce' + private readonly passwordField: string = 'input[id="password"]' + private readonly userNameField: string = 'input[id="user-name"]' + private readonly loginButton: string = 'input[id="login-button"]' + + constructor(page: Page) { + this.page = page; + } + + public async validateTitle(expectedTitle: string) { + const pageTitle = await this.page.title(); + if (pageTitle !== expectedTitle) { + throw new Error(`Expected title to be ${expectedTitle} but found ${pageTitle}`); + } + } + + public async loginAsUser(userName: string) { + await this.page.locator(this.userNameField).fill(userName) + await this.page.locator(this.passwordField).fill(this.password) + await this.page.locator(this.loginButton).click() + } +} \ No newline at end of file diff --git a/login.steps.ts b/login.steps.ts new file mode 100644 index 0000000..c3f4a6f --- /dev/null +++ b/login.steps.ts @@ -0,0 +1,15 @@ +import { Then } from '@cucumber/cucumber'; +import { getPage } from '../playwrightUtilities'; +import { Login } from '../pages/login.page'; + +Then('I should see the title {string}', async (expectedTitle) => { + await new Login(getPage()).validateTitle(expectedTitle); +}); + +Then('I will login as {string}', async (userName) => { + await new Login(getPage()).loginAsUser(userName); +}); + +Then('Validate Login error message', async () => { + await expect(errorMessage).toHaveText('Invalid username or password'); +}); \ No newline at end of file diff --git a/product.feature b/product.feature new file mode 100644 index 0000000..d8aa8a6 --- /dev/null +++ b/product.feature @@ -0,0 +1,14 @@ +Feature: Product Feature + + Background: + Given I open the "https://www.saucedemo.com/" page + + Scenario Outline: Validate product sort by price + Then I will login as 'standard_user' + Then I sort items by "" + Then I validate all 6 items are sorted correctly by "" + +Examples: + | sort | + | Price (low to high) | + | Price (high to low) | \ No newline at end of file diff --git a/product.page.ts b/product.page.ts new file mode 100644 index 0000000..33fbd8c --- /dev/null +++ b/product.page.ts @@ -0,0 +1,21 @@ +import { Page } from "@playwright/test" + +export class Product { + private readonly page: Page + private readonly addToCart: string = 'button[id="add-to-cart-sauce-labs-backpack"]' + + constructor(page: Page) { + this.page = page; + } + + public async addBackPackToCart() { + await this.page.locator(this.addToCart).click() + } + +public async sortItems(sortType: string) { + await this.page.selectOption(".product_sort_container", sortType); +} + public async waitForSelector(selector: string) { + await this.page.waitForSelector(selector); + +} \ No newline at end of file diff --git a/product.steps.ts b/product.steps.ts new file mode 100644 index 0000000..24a3fec --- /dev/null +++ b/product.steps.ts @@ -0,0 +1,7 @@ +import { Then } from '@cucumber/cucumber'; +import { getPage } from '../playwrightUtilities'; +import { Product } from '../pages/product.page'; + +Then('I will add the backpack to the cart', async () => { + await new Product(getPage()).addBackPackToCart(); +}); \ No newline at end of file diff --git a/purchase.feature b/purchase.feature new file mode 100644 index 0000000..60bbe32 --- /dev/null +++ b/purchase.feature @@ -0,0 +1,14 @@ +Feature: Purchase Feature + + Background: + Given I open the "https://www.saucedemo.com/" page + + ll add the backpack to the cart + Then Select the cart (top-right) + Then Select CheScenario: Validate successful purchase text + Then I will login as 'standard_user' + Then I wickout + Then Fill in the First Name, Last Name, and Zip/Postal Code + Then Select Continue + Then Select Finish + Then Validate the text 'Thank you for your order!' \ No newline at end of file diff --git a/purchase.page.ts b/purchase.page.ts new file mode 100644 index 0000000..ba79705 --- /dev/null +++ b/purchase.page.ts @@ -0,0 +1,72 @@ +import { Page } from "@playwright/test" + +export class Purchase { + private readonly page: Page + private readonly clickShoppingcartlink: string = '.shopping_cart_link' + + constructor(page: Page) { + this.page = page; + } + + public async clickShoppingCart() { + await this.page.locator(this.clickShoppingcartlink).click() + } + + public async clickCheckout() { + await this.page.click("#checkout"); + } + public async fillCheckoutInformation() { + await this.page.fill("#first-name", "John"); + await this.page.fill("#last-name", "Doe"); + await this.page.fill("#postal-code", "560001"); + } + public async clickContinue() { + await this.page.click("#continue"); + } + public async clickFinish() { + await this.page.click("#finish"); + } + public async validateTitle(expectedTitle: 'Thank you for your order!') {const pageTitle = await this.page.title(); + if (pageTitle == expectedTitle) { + throw new Error(`Expected title to be ${expectedTitle} but found ${pageTitle}`); + } + } + +public async sortItems(sortType: string) { + await this.page.selectOption(".product_sort_container", sortType); + + const priceElements = await this.page.locator(".inventory_item_price").allTextContents(); + const priceTexts = priceElements.map((text: string) => text.trim()); + + interface PriceValidation { + expectedCount: number; + actualCount: number; + } + + const priceValidation: PriceValidation = { + expectedCount: 6, + actualCount: priceTexts.length + }; + + if (priceValidation.actualCount !== priceValidation.expectedCount) { + throw new Error(`❌ Expected ${priceValidation.expectedCount} items but found: ${priceValidation.actualCount}`); + } + + const actualPrices = priceTexts.map((p: string) => parseFloat(p.replace("$", ""))); + + // Create expected sorted list + const expectedPrices = [...actualPrices]; + + if (sortType === "Price (low to high)") { + expectedPrices.sort((a, b) => a - b); + } else if (sortType === "Price (high to low)") { + expectedPrices.sort((a, b) => b - a); + } + + // Compare actual vs expected + if (JSON.stringify(actualPrices) !== JSON.stringify(expectedPrices)) { + throw new Error(`❌ Sorting failed for: ${sortType}\nActual: ${actualPrices}\nExpected: ${expectedPrices}`); + } + +} +} diff --git a/purchase.steps.ts b/purchase.steps.ts new file mode 100644 index 0000000..6c84cf0 --- /dev/null +++ b/purchase.steps.ts @@ -0,0 +1,64 @@ +import { Then } from '@cucumber/cucumber'; +import { getPage } from '../playwrightUtilities'; +import { Purchase } from '../pages/purchase.page'; + +Then('I will click on the shopping cart', async () => { + await new Purchase(getPage()).clickShoppingCart(); +}); + +Then('I will click on checkout', async () => { + await new Purchase(getPage()).clickCheckout(); +}); + +Then('I will fill the checkout information', async () => { + await new Purchase(getPage()).fillCheckoutInformation(); +}); + +Then('I will click on continue', async () => { + await new Purchase(getPage()).clickContinue(); +}); + +Then('I will click on finish', async () => { + await new Purchase(getPage()).clickFinish(); +}); + +Then('I should see the order confirmation message', async () => { + await new Purchase(getPage()).validateTitle('Thank you for your order!'); +}); + +Then('I sort the items by {string}', async (sortType) => { + const page = getPage(); + const priceTexts = await page.locator(".inventory_item_price").allTextContents(); + + if (priceTexts.length !== 6) { + throw new Error(`❌ Expected 6 items but found: ${priceTexts.length}`); + } + + // Convert to number array + const actualPrices = priceTexts.map(p => parseFloat(p.replace("$", ""))); + + // Create expected sorted list + const expectedPrices = [...actualPrices]; + + if (sortType === "Price (low to high)") { + expectedPrices.sort((a, b) => a - b); + } else if (sortType === "Price (high to low)") { + expectedPrices.sort((a, b) => b - a); + } + + // Compare actual vs expected + if (JSON.stringify(actualPrices) !== JSON.stringify(expectedPrices)) { + throw new Error(`❌ Sorting failed for: ${sortType}\nActual: ${actualPrices}\nExpected: ${expectedPrices}`); + } + + console.log(`✅ Sorting validated successfully for: ${sortType}`); +}); + +Then ('I validate the sorting of items', async () => { + const priceTexts = await this.page.locator(".inventory_item_price").allTextContents(); + const actualPrices = priceTexts.map((p: string) => parseFloat(p.replace("$", ""))); + const expectedPrices = [...actualPrices]; +}); + + +