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
5 changes: 2 additions & 3 deletions features/login.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ 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"
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 login error message "Epic sadface: Sorry, this user has been locked out."
20 changes: 13 additions & 7 deletions features/product.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ 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
Scenario Outline: Validate product sort by price <sort>
Then I will login as 'standard_user'
Then I will sort the products by "<sort>"
Then all 6 product prices should be sorted "<direction>"

Examples:
# TODO: extend the datatable to paramterize this test
| sort |
| sort | direction |
| Price (low to high) | ascending |
| Price (high to low) | descending |

Scenario: Validate cart reflects the selected item
Then I will login as 'standard_user'
Then I will add the backpack to the cart
Then the cart item count should be 1
18 changes: 9 additions & 9 deletions features/purchase.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ Feature: Purchase Feature
Background:
Given I open the "https://www.saucedemo.com/" page

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!'
Scenario: Validate successful purchase text
Then I will login as 'standard_user'
Then I will add the backpack to the cart
Then I will open the cart
Then I will checkout
Then I will provide checkout information with first name "Ada", last name "Lovelace", and postal code "10001"
Then I will continue checkout
Then I will finish checkout
Then I should see the order confirmation "Thank you for your order!"
10 changes: 9 additions & 1 deletion pages/login.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '[data-test="error"]'

constructor(page: Page) {
this.page = page;
Expand All @@ -23,4 +24,11 @@ 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 actualMessage = await this.page.locator(this.errorMessage).textContent()
if (actualMessage?.trim() !== expectedMessage) {
throw new Error(`Expected login error message to be "${expectedMessage}" but found "${actualMessage?.trim() ?? ''}"`)
}
}
}
85 changes: 84 additions & 1 deletion pages/product.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ 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 cartLink: string = '.shopping_cart_link'
private readonly cartBadge: string = '.shopping_cart_badge'
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 orderConfirmation: string = '[data-test="complete-header"]'
private readonly sortSelect: string = '[data-test="product-sort-container"]'
private readonly productPrice: string = '.inventory_item_price'

constructor(page: Page) {
this.page = page;
Expand All @@ -11,4 +22,76 @@ export class Product {
public async addBackPackToCart() {
await this.page.locator(this.addToCart).click()
}
}

public async openCart() {
await this.page.locator(this.cartLink).click()
}

public async checkout() {
await this.page.locator(this.checkoutButton).click()
}

public async fillCheckoutInformation(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 continueCheckout() {
await this.page.locator(this.continueButton).click()
}

public async finishCheckout() {
await this.page.locator(this.finishButton).click()
}

public async validateOrderConfirmation(expectedText: string) {
const actualText = await this.page.locator(this.orderConfirmation).textContent()
if (actualText?.trim() !== expectedText) {
throw new Error(`Expected order confirmation to be "${expectedText}" but found "${actualText?.trim() ?? ''}"`)
}
}

public async sortBy(sortLabel: string) {
const sortValues: Record<string, string> = {
'Price (low to high)': 'lohi',
'Price (high to low)': 'hilo',
}
const sortValue = sortValues[sortLabel]
if (!sortValue) {
throw new Error(`Unsupported product sort option: ${sortLabel}`)
}
await this.page.locator(this.sortSelect).selectOption(sortValue)
}

public async validatePricesAreSorted(direction: string, expectedCount: number) {
const prices = await this.page.locator(this.productPrice).allTextContents()
const numericPrices = prices.map((price) => Number.parseFloat(price.replace('$', '')))

if (numericPrices.length !== expectedCount) {
throw new Error(`Expected ${expectedCount} product prices but found ${numericPrices.length}`)
}

const isAscending = direction === 'ascending'
const isDescending = direction === 'descending'
if (!isAscending && !isDescending) {
throw new Error(`Unsupported sort direction: ${direction}`)
}

for (let index = 1; index < numericPrices.length; index += 1) {
const previous = numericPrices[index - 1]
const current = numericPrices[index]
const isCorrectOrder = isAscending ? previous <= current : previous >= current
if (!isCorrectOrder) {
throw new Error(`Expected prices to be sorted ${direction}, but found ${numericPrices.join(', ')}`)
}
}
}

public async validateCartItemCount(expectedCount: number) {
const actualCount = Number.parseInt((await this.page.locator(this.cartBadge).textContent())?.trim() ?? '0', 10)
if (actualCount !== expectedCount) {
throw new Error(`Expected cart item count to be ${expectedCount} but found ${actualCount}`)
}
}
}
6 changes: 5 additions & 1 deletion 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 login error message {string}', async (expectedMessage) => {
await new Login(getPage()).validateErrorMessage(expectedMessage);
});
38 changes: 37 additions & 1 deletion steps/product.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,40 @@ import { Product } from '../pages/product.page';

Then('I will add the backpack to the cart', async () => {
await new Product(getPage()).addBackPackToCart();
});
});

Then('I will open the cart', async () => {
await new Product(getPage()).openCart();
});

Then('I will checkout', async () => {
await new Product(getPage()).checkout();
});

Then('I will provide checkout information with first name {string}, last name {string}, and postal code {string}', async (firstName, lastName, postalCode) => {
await new Product(getPage()).fillCheckoutInformation(firstName, lastName, postalCode);
});

Then('I will continue checkout', async () => {
await new Product(getPage()).continueCheckout();
});

Then('I will finish checkout', async () => {
await new Product(getPage()).finishCheckout();
});

Then('I should see the order confirmation {string}', async (expectedText) => {
await new Product(getPage()).validateOrderConfirmation(expectedText);
});

Then('I will sort the products by {string}', async (sortLabel) => {
await new Product(getPage()).sortBy(sortLabel);
});

Then('all {int} product prices should be sorted {string}', async (expectedCount, direction) => {
await new Product(getPage()).validatePricesAreSorted(direction, expectedCount);
});

Then('the cart item count should be {int}', async (expectedCount) => {
await new Product(getPage()).validateCartItemCount(expectedCount);
});
Loading