From e15cd2192ef1918d33e4e1863ed6e518bf8198e7 Mon Sep 17 00:00:00 2001 From: dimu99 Date: Wed, 1 Apr 2026 14:03:13 -0400 Subject: [PATCH] adding modifications for login, product and purchase features --- features/login.feature | 7 +++--- features/product.feature | 7 +++++- features/purchase.feature | 8 ++++++- hooks/globalHooks.ts | 2 +- package-lock.json | 10 ++++----- package.json | 2 +- pages/login.page.ts | 8 +++++++ pages/product.page.ts | 21 ++++++++++++++++++ pages/purchase.page.ts | 46 +++++++++++++++++++++++++++++++++++++++ steps/login.steps.ts | 4 ++++ steps/product.steps.ts | 7 ++++++ steps/purchase.steps.ts | 27 +++++++++++++++++++++++ 12 files changed, 137 insertions(+), 12 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 fb9f1fa..a0d1e0d 100644 --- a/features/login.feature +++ b/features/login.feature @@ -4,9 +4,10 @@ Feature: Login Feature Given I open the "https://www.saucedemo.com/" page Scenario: Validate the login page title - # TODO: Fix this failing scenario - Then I should see the title "Labs Swag" + # TODO: Fix this failing scenario-added correct title + 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 + # TODO: Add a step to validate the error message received-added step to validate error msg + Then I should 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 8a7ceab..41bbc96 100644 --- a/features/product.feature +++ b/features/product.feature @@ -7,7 +7,12 @@ Feature: Product Feature Scenario Outline: Validate product sort by price Then I will login as 'standard_user' # TODO: Sort the items by + Then I will sort items by "" # TODO: Validate all 6 items are sorted correctly by price + Then I should see all 6 items sorted correctly by price "" Examples: # TODO: extend the datatable to paramterize this test - | sort | \ No newline at end of file + | sort | + | sort | order | + |Price (low to high) | asc | + |Price (low to high) | desc | \ No newline at end of file diff --git a/features/purchase.feature b/features/purchase.feature index 2863478..8f88590 100644 --- a/features/purchase.feature +++ b/features/purchase.feature @@ -11,4 +11,10 @@ Feature: Purchase Feature # 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 + # TODO: Validate the text 'Thank you for your order!' + Then I will open the cart + Then I will select Checkout + Then I will fill in the checkout info with "Patrice", "Jackson", and "11222" + Then I will select Continue + Then I will select Finish + Then I will validate the purchase message "Thank you for your order!" \ No newline at end of file diff --git a/hooks/globalHooks.ts b/hooks/globalHooks.ts index 5743ca8..6f3f345 100644 --- a/hooks/globalHooks.ts +++ b/hooks/globalHooks.ts @@ -1,7 +1,7 @@ import { After, Before, setDefaultTimeout } from "@cucumber/cucumber"; import { closeBrowser, initializeBrowser, initializePage } from "../playwrightUtilities"; -setDefaultTimeout(15000); +setDefaultTimeout(60000); Before( async () => { await initializeBrowser(); diff --git a/package-lock.json b/package-lock.json index b90d7c6..a95e18a 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": { @@ -11,7 +11,7 @@ "@types/node": "^20.10.3", "cucumber-html-reporter": "^7.1.1", "ts-node": "^10.9.1", - "typescript": "^5.3.2" + "typescript": "^5.9.3" } }, "node_modules/@babel/code-frame": { @@ -2469,9 +2469,9 @@ } }, "node_modules/typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "bin": { "tsc": "bin/tsc", diff --git a/package.json b/package.json index ed38f6f..3cbb616 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,6 @@ "@types/node": "^20.10.3", "cucumber-html-reporter": "^7.1.1", "ts-node": "^10.9.1", - "typescript": "^5.3.2" + "typescript": "^5.9.3" } } diff --git a/pages/login.page.ts b/pages/login.page.ts index 5a01614..1c5e882 100644 --- a/pages/login.page.ts +++ b/pages/login.page.ts @@ -1,11 +1,13 @@ 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"]' + private readonly errorMessage: string = 'h3[data-test="error"]' constructor(page: Page) { this.page = page; @@ -23,4 +25,10 @@ export class Login { await this.page.locator(this.passwordField).fill(this.password) await this.page.locator(this.loginButton).click() } + public async validateErrorMessage(expectedMessage: string) { + const errorText = await this.page.locator(this.errorMessage).textContent(); + if (errorText?.trim() !== expectedMessage) { + throw new Error(`Expected error message to be "${expectedMessage}" but found "${errorText?.trim()}"`); + } + } } \ No newline at end of file diff --git a/pages/product.page.ts b/pages/product.page.ts index 14bedb1..1e37a0a 100644 --- a/pages/product.page.ts +++ b/pages/product.page.ts @@ -3,6 +3,8 @@ 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 itemPrices: string = '[data-test="inventory-item-price"]'; constructor(page: Page) { this.page = page; @@ -11,4 +13,23 @@ export class Product { public async addBackPackToCart() { await this.page.locator(this.addToCart).click() } + public async sortItemsBy(sortOption: string) { + await this.page.locator(this.sortDropdown).selectOption({ label: sortOption }); + } + + public async validateItemsSortedByPrice(order: string) { + const priceElements = await this.page.locator(this.itemPrices).allTextContents(); + if (priceElements.length !== 6) { + throw new Error(`Expected 6 items but found ${priceElements.length}`); + } + const prices = priceElements.map(p => parseFloat(p.replace('$', ''))); + for (let i = 0; i < prices.length - 1; i++) { + if (order === 'asc' && prices[i] > prices[i + 1]) { + throw new Error(`Items are not sorted by price (low to high). Found ${prices[i]} before ${prices[i + 1]}`); + } + if (order === 'desc' && prices[i] < prices[i + 1]) { + throw new Error(`Items are not sorted by price (high to low). Found ${prices[i]} before ${prices[i + 1]}`); + } + } + } } \ No newline at end of file diff --git a/pages/purchase.page.ts b/pages/purchase.page.ts new file mode 100644 index 0000000..1881d86 --- /dev/null +++ b/pages/purchase.page.ts @@ -0,0 +1,46 @@ +import { Page } from "@playwright/test" + +export class Purchase { + private readonly page: Page + private readonly cartLink: string = 'a.shopping_cart_link' + private readonly checkoutButton: string = 'button[id="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 continueButton: string = 'input[id="continue"]' + private readonly finishButton: string = 'button[id="finish"]' + private readonly completeHeader: string = 'h2.complete-header' + + constructor(page: Page) { + this.page = page; + } + + public async openCart() { + await this.page.locator(this.cartLink).click() + } + + public async selectCheckout() { + await this.page.locator(this.checkoutButton).click() + } + + public async fillCheckoutInfo(firstName: string, lastName: string, postalCode: string) { + await this.page.locator(this.firstNameField).fill(firstName) + await this.page.locator(this.lastNameField).fill(lastName) + await this.page.locator(this.postalCodeField).fill(postalCode) + } + + public async selectContinue() { + await this.page.locator(this.continueButton).click() + } + + public async selectFinish() { + await this.page.locator(this.finishButton).click() + } + + public async validateThankYouText(expectedText: string) { + const text = await this.page.locator(this.completeHeader).textContent() + if (text?.trim() !== expectedText) { + throw new Error(`Expected purchase message to be "${expectedText}" but found "${text?.trim()}"`) + } + } +} diff --git a/steps/login.steps.ts b/steps/login.steps.ts index c2aa0d8..9610de9 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 the error message {string}', async (expectedMessage) => { + await new Login(getPage()).validateErrorMessage(expectedMessage); }); \ No newline at end of file diff --git a/steps/product.steps.ts b/steps/product.steps.ts index bb52fb9..119e538 100644 --- a/steps/product.steps.ts +++ b/steps/product.steps.ts @@ -4,4 +4,11 @@ import { Product } from '../pages/product.page'; Then('I will add the backpack to the cart', async () => { await new Product(getPage()).addBackPackToCart(); +}); +Then('I will sort the items by {string}', async (sortOption) => { + await new Product(getPage()).sortItemsBy(sortOption); +}); + +Then('I should see all 6 items sorted by price {string}', async (order) => { + await new Product(getPage()).validateItemsSortedByPrice(order); }); \ No newline at end of file diff --git a/steps/purchase.steps.ts b/steps/purchase.steps.ts new file mode 100644 index 0000000..5b6d094 --- /dev/null +++ b/steps/purchase.steps.ts @@ -0,0 +1,27 @@ +import { Then } from '@cucumber/cucumber'; +import { getPage } from '../playwrightUtilities'; +import { Purchase } from '../pages/purchase.page'; + +Then('I will open the cart', async () => { + await new Purchase(getPage()).openCart(); +}); + +Then('I will select Checkout', async () => { + await new Purchase(getPage()).selectCheckout(); +}); + +Then('I will fill in the checkout info with {string}, {string}, and {string}', async (firstName, lastName, postal) => { + await new Purchase(getPage()).fillCheckoutInfo(firstName, lastName, postal); +}); + +Then('I will select Continue', async () => { + await new Purchase(getPage()).selectContinue(); +}); + +Then('I will select Finish', async () => { + await new Purchase(getPage()).selectFinish(); +}); + +Then('I will validate the purchase message {string}', async (expectedMessage) => { + await new Purchase(getPage()).validateThankYouText(expectedMessage); +});