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
9 changes: 6 additions & 3 deletions features/login.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ 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 error message "Epic sadface: Sorry, this user has been locked out."

Scenario: Validate invalid login
Then I will login as 'invalid_user'
Then I should see the error message "Epic sadface: Username and password do not match any user in this service"
15 changes: 8 additions & 7 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
Scenario Outline: Validate product sort by price <sort>
Then I will login as 'standard_user'
Then I sort products by "<sort>"
Then products should be sorted "<order>"

Examples:
# TODO: extend the datatable to paramterize this test
| sort |
| sort | order |
| Price (low to high) | asc |
| Price (high to low) | desc |
17 changes: 8 additions & 9 deletions features/purchase.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ 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 open the cart
Then I checkout the product
Then I enter checkout details "John" "Doe" "560001"
Then I finish the purchase
Then I should see confirmation message "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 = 'h3[data-test="error"]'

constructor(page: Page) {
this.page = page;
Expand All @@ -14,7 +15,7 @@ export class Login {
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}`);
throw new Error(Expected title to be ${expectedTitle} but found ${pageTitle});
}
}

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).innerText()
if (actualMessage !== expectedMessage) {
throw new Error(Expected error message: ${expectedMessage}, but got: ${actualMessage})
}
}
}
17 changes: 17 additions & 0 deletions pages/product.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '.product_sort_container'
private readonly prices = '.inventory_item_price'

constructor(page: Page) {
this.page = page;
Expand All @@ -11,4 +13,19 @@ export class Product {
public async addBackPackToCart() {
await this.page.locator(this.addToCart).click()
}
async sortBy(option: string) {
await this.page.selectOption(this.sortDropdown, { label: option })
}

async validateSorted(order: string) {
const priceTexts = await this.page.locator(this.prices).allTextContents()

const prices = priceTexts.map(p => parseFloat(p.replace('$', '')))

const sorted = [...prices].sort((a, b) => order === 'asc' ? a - b : b - a)

if (JSON.stringify(prices) !== JSON.stringify(sorted)) {
throw new Error(Products are not sorted correctly: ${order})
}
}
}
47 changes: 47 additions & 0 deletions pages/purchase.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Page } from "@playwright/test"

export class Purchase {
private readonly page: Page

private readonly cartIcon = '.shopping_cart_link'
private readonly checkoutBtn = '#checkout'
private readonly firstName = '#first-name'
private readonly lastName = '#last-name'
private readonly zip = '#postal-code'
private readonly continueBtn = '#continue'
private readonly finishBtn = '#finish'
private readonly successMsg = '.complete-header'

constructor(page: Page) {
this.page = page;
}

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

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

async enterDetails(fn: string, ln: string, zip: string) {
await this.page.fill(this.firstName, fn)
await this.page.fill(this.lastName, ln)
await this.page.fill(this.zip, zip)
}

async continue() {
await this.page.locator(this.continueBtn).click()
}

async finish() {
await this.page.locator(this.finishBtn).click()
}

async validateSuccessMessage(expected: string) {
const actual = await this.page.locator(this.successMsg).innerText()
if (actual !== expected) {
throw new Error(Expected: ${expected}, Got: ${actual})
}
}
}
8 changes: 8 additions & 0 deletions steps/login.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,12 @@ 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 (expectedMessage) => {
await new Login(getPage()).validateErrorMessage(expectedMessage);
});

Then('I click login without credentials', async () => {
await getPage().locator('#login-button').click();
});
9 changes: 9 additions & 0 deletions steps/product.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,13 @@ import { Product } from '../pages/product.page';

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


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

Then('products should be sorted {string}', async (order) => {
await new Product(getPage()).validateSorted(order);
});
32 changes: 32 additions & 0 deletions steps/purchase.steps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Then } from '@cucumber/cucumber';
import { getPage } from '../playwrightUtilities';
import { Purchase } from '../pages/purchase.page';

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

Then('I checkout the product', async () => {
await new Purchase(getPage()).checkout();
});

Then(
'I enter checkout details {string} {string} {string}',
async (firstName, lastName, zip) => {
await new Purchase(getPage()).enterDetails(firstName, lastName, zip);
}
);

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

Then('I finish the purchase', async () => {
const purchase = new Purchase(getPage());
await purchase.continue();
await purchase.finish();
});

Then('I should see confirmation message {string}', async (expected) => {
await new Purchase(getPage()).validateSuccessMessage(expected);
});
Loading