From d56c0f00eaeecf8228f2a597073ceacc5f48ca7f Mon Sep 17 00:00:00 2001 From: Maloo <2178890@cognizant.com> Date: Tue, 23 Jun 2026 16:36:46 +0530 Subject: [PATCH] update, fix and added new test --- features/login.feature | 4 +-- features/product.feature | 11 ++++--- features/purchase.feature | 17 +++++++---- hooks/globalHooks.ts | 6 ++-- package-lock.json | 2 +- pages/login.page.ts | 8 ++++- pages/product.page.ts | 33 +++++++++++++++++++- pages/purchase.page.ts | 64 +++++++++++++++++++++++++++++++++++++++ steps/login.steps.ts | 4 +++ steps/product.steps.ts | 8 +++++ steps/purchase.steps.ts | 27 +++++++++++++++++ 11 files changed, 165 insertions(+), 19 deletions(-) create mode 100644 pages/purchase.page.ts create mode 100644 steps/purchase.steps.ts diff --git a/features/login.feature b/features/login.feature index fb9f1fa5..4e205110 100644 --- a/features/login.feature +++ b/features/login.feature @@ -5,8 +5,8 @@ Feature: Login Feature Scenario: Validate the login page title # TODO: Fix this failing scenario - Then I should see the title "Labs Swag" + Then I should see the title "Swag Labs" Scenario: Validate login error message Then I will login as 'locked_out_user' - # TODO: Add a step to validate the error message received \ No newline at end of file + Then I should see the error message "Epic sadface: Sorry, this user has been locked out." \ No newline at end of file diff --git a/features/product.feature b/features/product.feature index 8a7ceab9..facbd946 100644 --- a/features/product.feature +++ b/features/product.feature @@ -3,11 +3,12 @@ Feature: Product Feature Background: Given I open the "https://www.saucedemo.com/" page - # Create a datatable to validate the Price (high to low) and Price (low to high) sort options (top-right) using a Scenario Outline Scenario Outline: Validate product sort by price Then I will login as 'standard_user' - # TODO: Sort the items by - # TODO: Validate all 6 items are sorted correctly by price + Then I sort the items by "" + Then I should see all 6 items sorted by price "" Examples: - # TODO: extend the datatable to paramterize this test - | sort | \ No newline at end of file + | sort | direction | + | Price (low to high) | asc | + | Price (high to low) | desc | + diff --git a/features/purchase.feature b/features/purchase.feature index 28634789..fe6cc1a4 100644 --- a/features/purchase.feature +++ b/features/purchase.feature @@ -6,9 +6,14 @@ Feature: Purchase Feature Scenario: Validate successful purchase text Then I will login as 'standard_user' Then I will add the backpack to the cart - # TODO: Select the cart (top-right) - # TODO: Select Checkout - # TODO: Fill in the First Name, Last Name, and Zip/Postal Code - # TODO: Select Continue - # TODO: Select Finish - # TODO: Validate the text 'Thank you for your order!' \ No newline at end of file + When I select the cart and then click checkout + And I fill in the First Name, Last Name, and Zip Code and click Continue + And Then click Finish + Then I validate the text 'Thank you for your order!' + +Scenario: Validate product price is same till finish the order + Then I will login as 'standard_user' + Then I will add the backpack to the cart + When Get the price and click checkout + When I fill in the First Name, Last Name, and Zip Code and click Continue + Then Validate the price is same in checkout page \ No newline at end of file diff --git a/hooks/globalHooks.ts b/hooks/globalHooks.ts index 5743ca8e..640de0a2 100644 --- a/hooks/globalHooks.ts +++ b/hooks/globalHooks.ts @@ -1,13 +1,13 @@ import { After, Before, setDefaultTimeout } from "@cucumber/cucumber"; import { closeBrowser, initializeBrowser, initializePage } from "../playwrightUtilities"; -setDefaultTimeout(15000); +setDefaultTimeout(40000); -Before( async () => { +Before({ timeout: 60 * 1000 }, async () => { await initializeBrowser(); await initializePage(); }) -After( async () => { +After({ timeout: 60 * 1000 }, async () => { await closeBrowser(); }) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b90d7c6c..b5c47aa5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "Playwright-Project", + "name": "Playwright-Cucumber-Exercise", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/pages/login.page.ts b/pages/login.page.ts index 5a01614b..4e1e3890 100644 --- a/pages/login.page.ts +++ b/pages/login.page.ts @@ -1,4 +1,4 @@ -import { Page } from "@playwright/test" +import { Page, expect } from "@playwright/test" export class Login { private readonly page: Page @@ -6,6 +6,7 @@ export class Login { private readonly passwordField: string = 'input[id="password"]' private readonly userNameField: string = 'input[id="user-name"]' private readonly loginButton: string = 'input[id="login-button"]' + private readonly errorMessage: string = 'h3[data-test="error"]' constructor(page: Page) { this.page = page; @@ -23,4 +24,9 @@ export class Login { await this.page.locator(this.passwordField).fill(this.password) await this.page.locator(this.loginButton).click() } + + public async validateErrorMessage(expectedErrorMessage: string) { + const actualErrorMessage = await this.page.locator(this.errorMessage).textContent(); + expect(actualErrorMessage).toEqual(expectedErrorMessage); + } } \ No newline at end of file diff --git a/pages/product.page.ts b/pages/product.page.ts index 14bedb1b..693dc416 100644 --- a/pages/product.page.ts +++ b/pages/product.page.ts @@ -3,6 +3,9 @@ import { Page } from "@playwright/test" export class Product { private readonly page: Page private readonly addToCart: string = 'button[id="add-to-cart-sauce-labs-backpack"]' + private readonly sortDropdown: string = '[data-test="product-sort-container"]' + private readonly itemPrice: string = '.inventory_item_price' + private readonly expectedItemCount: number = 6 constructor(page: Page) { this.page = page; @@ -11,4 +14,32 @@ export class Product { public async addBackPackToCart() { await this.page.locator(this.addToCart).click() } -} \ No newline at end of file + + public async sortItemsBy(sortLabel: string) { + await this.page.locator(this.sortDropdown).selectOption(sortLabel, {timeout: 30000}); + } + + public async validatePricesSorted(direction: 'asc' | 'desc') { + const priceTexts = await this.page.locator(this.itemPrice).allTextContents() + + if (priceTexts.length !== this.expectedItemCount) { + throw new Error(`Expected ${this.expectedItemCount} items but found ${priceTexts.length}`) + } + + const prices = priceTexts.map((text) => { + const parsed = parseFloat(text.replace('$', '')) + if (Number.isNaN(parsed)) { + throw new Error(`Could not parse price from "${text}"`) + } + return parsed + }) + + const expected = [...prices].sort((a, b) => direction === 'asc' ? a - b : b - a) + + for (let i = 0; i < prices.length; i++) { + if (prices[i] !== expected[i]) { + throw new Error(`Prices are not sorted ${direction}.`) + } + } + } +} diff --git a/pages/purchase.page.ts b/pages/purchase.page.ts new file mode 100644 index 00000000..eb61deee --- /dev/null +++ b/pages/purchase.page.ts @@ -0,0 +1,64 @@ +import { Page, expect } from "@playwright/test" + +export let price: number; + +export class Purchase { + private readonly page: Page + private readonly cartBtn: string = 'a.shopping_cart_link' + private readonly checkoutBtn: string = '[data-test="checkout"]' + private readonly firstNameField: string = 'input[id="first-name"]' + private readonly lastNameField: string = 'input[id="last-name"]' + private readonly postalCodeField: string = 'input[id="postal-code"]' + private readonly continueBtn: string = '[data-test="continue"]' + private readonly finishBtn: string = '[data-test="finish"]' + private readonly completeHeader: string = '[data-test="complete-header"]' + private readonly inventoryItemPrice: string = '[data-test="inventory-item-price"]' + + constructor(page: Page) { + this.page = page; + } + public async selectCartAndCheckout() { + await this.page.locator(this.cartBtn).click(); + await this.page.locator(this.checkoutBtn).click(); + } + + public async fillInShippingInformationAndClickContinue() { + await this.page.locator(this.firstNameField).fill('John'); + await this.page.locator(this.lastNameField).fill('Doe'); + await this.page.locator(this.postalCodeField).fill('12345'); + await this.page.locator(this.continueBtn).click(); + } + + public async selectFinish() { + await this.page.locator(this.finishBtn).click(); + } + + public async validateCompleteText(expectedText: string) { + const actualText = await this.page.locator(this.completeHeader).textContent(); + expect(actualText).toEqual(expectedText); + } + + public async getPriceAndClickCheckout() { + const priceText = await this.page.locator(this.inventoryItemPrice).first().textContent(); + if (!priceText) { + throw new Error('Could not find price text'); + } + price = parseFloat(priceText.replace('$', '')); + if (Number.isNaN(price)) { + throw new Error(`Could not parse price from "${priceText}"`); + } + await this.selectCartAndCheckout(); + } + + public async validatePriceInCheckout() { + const checkoutPriceText = await this.page.locator(this.inventoryItemPrice).first().textContent(); + if (!checkoutPriceText) { + throw new Error('Could not find price text in checkout'); + } + const checkoutPrice = parseFloat(checkoutPriceText.replace('$', '')); + if (Number.isNaN(checkoutPrice)) { + throw new Error(`Could not parse price from "${checkoutPriceText}"`); + } + expect(checkoutPrice).toEqual(price); + } +} diff --git a/steps/login.steps.ts b/steps/login.steps.ts index c2aa0d80..a18aaa84 100644 --- a/steps/login.steps.ts +++ b/steps/login.steps.ts @@ -8,4 +8,8 @@ Then('I should see the title {string}', async (expectedTitle) => { Then('I will login as {string}', async (userName) => { await new Login(getPage()).loginAsUser(userName); +}); + +Then('I should see the error message {string}', async (expectedErrorMessage) => { + await new Login(getPage()).validateErrorMessage(expectedErrorMessage); }); \ No newline at end of file diff --git a/steps/product.steps.ts b/steps/product.steps.ts index bb52fb98..d50a8828 100644 --- a/steps/product.steps.ts +++ b/steps/product.steps.ts @@ -4,4 +4,12 @@ import { Product } from '../pages/product.page'; Then('I will add the backpack to the cart', async () => { await new Product(getPage()).addBackPackToCart(); +}); + +Then('I sort the items by {string}', async (sortOption: string) => { + await new Product(getPage()).sortItemsBy(sortOption); +}); + +Then('I should see all 6 items sorted by price {string}', async (direction: string) => { + await new Product(getPage()).validatePricesSorted(direction as 'asc' | 'desc'); }); \ No newline at end of file diff --git a/steps/purchase.steps.ts b/steps/purchase.steps.ts new file mode 100644 index 00000000..2a5c76fa --- /dev/null +++ b/steps/purchase.steps.ts @@ -0,0 +1,27 @@ +import { Then, When } from '@cucumber/cucumber'; +import { getPage } from '../playwrightUtilities'; +import { Purchase } from '../pages/purchase.page'; + +When('I select the cart and then click checkout', async () => { + await new Purchase(getPage()).selectCartAndCheckout(); +}); + +When('I fill in the First Name, Last Name, and Zip Code and click Continue', async () => { + await new Purchase(getPage()).fillInShippingInformationAndClickContinue(); +}); + +When('Then click Finish', async () => { + await new Purchase(getPage()).selectFinish(); +}); + +Then('I validate the text {string}', async (expectedText: string) => { + await new Purchase(getPage()).validateCompleteText(expectedText); +}); + +Then('Get the price and click checkout', async () => { + await new Purchase(getPage()).getPriceAndClickCheckout(); +}); + +Then('Validate the price is same in checkout page', async () => { + await new Purchase(getPage()).validatePriceInCheckout(); +});