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
28 changes: 24 additions & 4 deletions features/login.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,29 @@ 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 will login as "locked_out_user"
Then I should see the error message "Epic sadface: Sorry, this user has been locked out."

Scenario: Login with invalid username
Then I will login with username "invalid_user_name" and password "secret_sauce"
Then I should see the error message "Epic sadface: Username and password do not match any user in this service"

Scenario: Login with invalid password
Then I will login with username "standard_user" and password "wrong_password"
Then I should see the error message "Epic sadface: Username and password do not match any user in this service"

Scenario: Login with empty username
Then I will login with username "" and password "secret_sauce"
Then I should see the error message "Epic sadface: Username is required"

Scenario: Login with empty password
Then I will login with username "standard_user" and password ""
Then I should see the error message "Epic sadface: Password is required"

Scenario: Login with empty username and password
Then I will login with username "" and password ""
Then I should see the error message "Epic sadface: Username is required"

24 changes: 16 additions & 8 deletions features/product.feature
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
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
Examples:
# TODO: extend the datatable to paramterize this test
| sort |
Scenario Outline: Validate product sorting

Then I will login as "standard_user"
When I sort products by "<sort>"
Then all products should be sorted by "<sort>"

Examples:
| sort |
| Price (low to high) |
| Price (high to low) |
| Name (A to Z) |
| Name (Z to A) |



16 changes: 13 additions & 3 deletions features/purchase.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,21 @@ Feature: Purchase Feature
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

Then I will login as 'standard_user'
Then I will add the backpack to the cart

Then I open the cart
Then the cart should contain "Sauce Labs Backpack" with price "$29.99" and quantity "1"
Then I proceed to checkout
Then I fill in checkout information
Then I continue checkout
Then I finish the purchase
Then I should see the success message

# 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!'
# TODO: Validate the text 'Thank you for your order!'
9 changes: 8 additions & 1 deletion package-lock.json

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

35 changes: 35 additions & 0 deletions pages/cart.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Page, expect } from '@playwright/test';

export class Cart {

constructor(private page: Page) {}

private cartIcon = '.shopping_cart_link';
private checkoutButton = '[data-test="checkout"]';
private cartItemName = '.inventory_item_name';
private cartItemPrice = '.inventory_item_price';
private cartItemQuantity = '.cart_quantity';


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

public async goToCheckout() {
await this.page.click(this.checkoutButton);
}

public async validateCartItem(expectedName: string, expectedPrice: string, expectedQty: string) {
const name = await this.page.locator(this.cartItemName).textContent();
const price = await this.page.locator(this.cartItemPrice).textContent();
const qty = await this.page.locator(this.cartItemQuantity).textContent();

expect(name?.trim()).toBe(expectedName);
expect(price?.trim()).toBe(expectedPrice);
expect(qty?.trim()).toBe(expectedQty);
}

}



21 changes: 21 additions & 0 deletions pages/checkout.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Page } from '@playwright/test';

export class Checkout {

constructor(private page: Page) {}

private firstName = '[data-test="firstName"]';
private lastName = '[data-test="lastName"]';
private postalCode = '[data-test="postalCode"]';
private continueButton = '[data-test="continue"]';

public async fillInformation(first: string, last: string, zip: string) {
await this.page.fill(this.firstName, first);
await this.page.fill(this.lastName, last);
await this.page.fill(this.postalCode, zip);
}

public async continue() {
await this.page.click(this.continueButton);
}
}
13 changes: 13 additions & 0 deletions pages/checkoutComplete.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Page, expect } from '@playwright/test';

export class CheckoutComplete {

constructor(private page: Page) {}

private successHeader = '.complete-header';

public async validateSuccessMessage() {
await expect(this.page.locator(this.successHeader)).toHaveText('Thank you for your order!');
}
}

12 changes: 12 additions & 0 deletions pages/checkoutOverview.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Page } from '@playwright/test';

export class CheckoutOverview {

constructor(private page: Page) {}

private finishButton = '[data-test="finish"]';

public async finish() {
await this.page.click(this.finishButton);
}
}
14 changes: 13 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 errorMessageLocator: string = '[data-test="error"]'

constructor(page: Page) {
this.page = page;
Expand All @@ -23,4 +24,15 @@ export class Login {
await this.page.locator(this.passwordField).fill(this.password)
await this.page.locator(this.loginButton).click()
}

public async validateErrorMessage(expected: string) {
await expect(this.page.locator(this.errorMessageLocator)).toHaveText(expected);
}

public async login(username: string, password: string) {
await this.page.locator(this.userNameField).fill(username);
await this.page.locator(this.passwordField).fill(password);
await this.page.locator(this.loginButton).click();
}

}
42 changes: 41 additions & 1 deletion pages/product.page.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { Page } from "@playwright/test"
import { Page, expect } 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 priceLabels:string = '.inventory_item_price'
private readonly inventoryItemName: string = '.inventory_item_name'


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

public async sortBy(option: string) {
await this.page.locator(this.sortDropdown).selectOption({ label: option })
}

private async validatePriceSorting(option: string) {
const prices = await this.page.locator(this.priceLabels).allTextContents();
const numPrices = prices.map(p => Number(p.replace('$', '')));

const sortedPrices = [...numPrices].sort((a, b) =>
option.includes('low to high') ? a - b : b - a
);

expect(numPrices).toEqual(sortedPrices);
}

private async validateNameSorting(option: string) {
const names = await this.page.locator(this.inventoryItemName).allTextContents();
const sortedNames = [...names].sort((a, b) =>
option.includes("A to Z")
? a.localeCompare(b)
: b.localeCompare(a)
);

expect(names).toEqual(sortedNames);
}

public async validateSorting(option: string) {
if (option.includes("low to high") || option.includes("high to low")) {
await this.validatePriceSorting(option);
} else {
await this.validateNameSorting(option);
}

}

}
10 changes: 9 additions & 1 deletion 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 will login with username {string} and password {string}', async (username, password) => {
await new Login(getPage()).login(username, password);
});
16 changes: 14 additions & 2 deletions steps/product.steps.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import { Then } from '@cucumber/cucumber';
import { Then, When } 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();
});
});


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


Then('all products should be sorted by {string}', async (sortOption) => {
await new Product(getPage()).validateSorting(sortOption);
});


Loading
Loading