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
4 changes: 2 additions & 2 deletions features/login.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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
Then I should see the error message "Epic sadface: Sorry, this user has been locked out."
11 changes: 6 additions & 5 deletions features/product.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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 <sort>
Then I will login as 'standard_user'
# TODO: Sort the items by <sort>
# TODO: Validate all 6 items are sorted correctly by price
Then I sort the items by "<sort>"
Then I should see all 6 items sorted by price "<direction>"
Examples:
# TODO: extend the datatable to paramterize this test
| sort |
| sort | direction |
| Price (low to high) | asc |
| Price (high to low) | desc |

17 changes: 11 additions & 6 deletions features/purchase.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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!'
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
6 changes: 3 additions & 3 deletions hooks/globalHooks.ts
Original file line number Diff line number Diff line change
@@ -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();
})
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion pages/login.page.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Page } from "@playwright/test"
import { Page, expect } 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;
Expand All @@ -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);
}
}
33 changes: 32 additions & 1 deletion pages/product.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -11,4 +14,32 @@ export class Product {
public async addBackPackToCart() {
await this.page.locator(this.addToCart).click()
}
}

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}.`)
}
}
}
}
64 changes: 64 additions & 0 deletions pages/purchase.page.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
4 changes: 4 additions & 0 deletions steps/login.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
8 changes: 8 additions & 0 deletions steps/product.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
27 changes: 27 additions & 0 deletions steps/purchase.steps.ts
Original file line number Diff line number Diff line change
@@ -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();
});
Loading