Skip to content
Open
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
6 changes: 6 additions & 0 deletions common.steps.ts
Original file line number Diff line number Diff line change
@@ -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);
});
13 changes: 13 additions & 0 deletions login.feature
Original file line number Diff line number Diff line change
@@ -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

26 changes: 26 additions & 0 deletions login.page.ts
Original file line number Diff line number Diff line change
@@ -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()
}
}
15 changes: 15 additions & 0 deletions login.steps.ts
Original file line number Diff line number Diff line change
@@ -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');
});
14 changes: 14 additions & 0 deletions product.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Feature: Product Feature

Background:
Given I open the "https://www.saucedemo.com/" page

Scenario Outline: Validate product sort by price <sort>
Then I will login as 'standard_user'
Then I sort items by "<sort>"
Then I validate all 6 items are sorted correctly by "<sort>"

Examples:
| sort |
| Price (low to high) |
| Price (high to low) |
21 changes: 21 additions & 0 deletions product.page.ts
Original file line number Diff line number Diff line change
@@ -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);

}
7 changes: 7 additions & 0 deletions product.steps.ts
Original file line number Diff line number Diff line change
@@ -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();
});
14 changes: 14 additions & 0 deletions purchase.feature
Original file line number Diff line number Diff line change
@@ -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!'
72 changes: 72 additions & 0 deletions purchase.page.ts
Original file line number Diff line number Diff line change
@@ -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}`);
}

}
}
64 changes: 64 additions & 0 deletions purchase.steps.ts
Original file line number Diff line number Diff line change
@@ -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];
});



Loading