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
17 changes: 12 additions & 5 deletions features/login.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@ 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
Scenario Outline: Login validation as different users
Then I will login as '<username>' with this password '<password>'
Then I should see this result '<result>'


Examples:
| username | password | result |
| standard_user | secret_sauce | inventory page |
| locked_out_user | secret_sauce | error message |
| invalid_user | wrong_pass | error message |
| | | username required |
10 changes: 10 additions & 0 deletions features/logout.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Feature: Logout Feature

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

Scenario: Validate that a user is able to logout of the application
Then I logged out of the application
Then I should see the title "Swag Labs"

11 changes: 5 additions & 6 deletions features/product.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ 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 will login
Then I will sort by '<sort>', and the results should be '<prices>'
Examples:
# TODO: extend the datatable to paramterize this test
| sort |
| sort | prices |
| Price (high to low) | 49.99, 29.99, 15.99, 15.99, 9.99, 7.99 |
| Price (low to high) | 7.99 ,9.99, 15.99, 15.99, 29.99, 49.99 |
12 changes: 5 additions & 7 deletions features/purchase.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ 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 login
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!'
Then I will go to my shopping cart
Then I will start to checkout
Then I will fill in my information
Then I will checkout
20 changes: 19 additions & 1 deletion package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"devDependencies": {
"@cucumber/cucumber": "^10.0.1",
"@cucumber/pretty-formatter": "^1.0.0",
"@faker-js/faker": "^10.3.0",
"@playwright/test": "^1.40.1",
"@types/node": "^20.10.3",
"cucumber-html-reporter": "^7.1.1",
Expand Down
32 changes: 32 additions & 0 deletions pages/checkout.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {expect, Page} from "@playwright/test"
import { faker } from '@faker-js/faker'

export class Checkout {
private readonly page: Page
private readonly checkoutButton: string = 'button[data-test="checkout"]'
private readonly checkoutFirstName: string = 'input[data-test="firstName"]'
private readonly checkoutLastName: string = 'input[data-test="lastName"]'
private readonly checkoutZipCode: string = 'input[data-test="postalCode"]'
private readonly continueCheckout: string = 'input[data-test="continue"]'
private readonly finishCheckout: string = 'button[data-test="finish"]'
private readonly thankYouMessage: string = 'h2[data-test="complete-header"]'

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

public async startCheckout(): Promise<void> {
await this.page.locator(this.checkoutButton).click();
}

public async fillInCheckout(): Promise<void> {
await this.page.locator(this.checkoutFirstName).fill(faker.person.firstName());
await this.page.locator(this.checkoutLastName).fill(faker.person.lastName());
await this.page.locator(this.checkoutZipCode).fill(faker.location.zipCode());
}
public async checkoutItems(): Promise<void> {
await this.page.locator(this.continueCheckout).click();
await this.page.locator(this.finishCheckout).click();
await expect(this.page.locator(this.thankYouMessage)).toHaveText('Thank you for your order!')
}
}
24 changes: 17 additions & 7 deletions pages/login.page.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
import { Page } from "@playwright/test"
import {expect, Page} from "@playwright/test"
import { Product } from "./product.page";
import {getPage} from "../playwrightUtilities";

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 errorField: string = 'h3[data-test="error"]'

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

public async validateTitle(expectedTitle: string) {
public async validateTitle(expectedTitle: string): Promise<void> {
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()
public async loginAsUser(username: string, password: string): Promise<void> {
await this.page.locator(this.userNameField).fill(username);
await this.page.locator(this.passwordField).fill(password);
await this.page.locator(this.loginButton).click();
}

public async validateErrorMessage(result: string): Promise<void> {
if (result === 'inventory page') {
await new Product(getPage()).validateTitle();
} else if (result === 'error message' || result === 'username required') {
await expect(this.page.locator(this.errorField)).toBeVisible();
}
}
}
15 changes: 15 additions & 0 deletions pages/logout.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Page} from "@playwright/test"

export class Logout {
private readonly page: Page
private readonly hamburgerMenu: string = 'button[id="react-burger-menu-btn"]'
private readonly logoutLink: string = 'a[data-test="logout-sidebar-link"]'
constructor(page: Page) {
this.page = page;
}

public async logout(): Promise<void> {
await this.page.locator(this.hamburgerMenu).click();
await this.page.locator(this.logoutLink).click();
}
}
30 changes: 28 additions & 2 deletions pages/product.page.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
import { Page } from "@playwright/test"
import {expect, Page} from "@playwright/test"

export class Product {
private readonly page: Page
private readonly productTitle: string = 'span[data-test="title"]'
private readonly addToCart: string = 'button[id="add-to-cart-sauce-labs-backpack"]'
private readonly shoppingCart: string = 'a[data-test="shopping-cart-link"]'
private readonly productSort: string = 'select[data-test="product-sort-container"]'
private readonly products: string = 'div[data-test="inventory-item-price"]'

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

public async addBackPackToCart() {
public async validateTitle(): Promise<void> {
await expect(this.page.locator(this.productTitle)).toBeVisible();
}
public async addBackPackToCart(): Promise<void> {
await this.page.locator(this.addToCart).click()
}

public async selectCart(): Promise<void> {
await this.page.locator(this.shoppingCart).click();
}

public async selectDropdown(sortOrder: string): Promise<void> {
await this.page.locator(this.productSort).selectOption(sortOrder);
}

public async sortItems(prices: string): Promise<void> {
const priceElements :string[] = await this.page.locator(this.products).allTextContents();
const actualPrices: number[] = priceElements.map((p: string) :number => parseFloat(p.replace('$', '')));
const expectedPrices: number[] = prices.split(',').map(Number);

expect(actualPrices.length).toBe(6);
expectedPrices.forEach((expectedPrice: number, index: number): void => {
expect(actualPrices[index]).toBe(expectedPrice);
});
}
}
15 changes: 15 additions & 0 deletions steps/checkout.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 { Checkout } from '../pages/checkout.page';

Then('I will start to checkout', async () => {
await new Checkout(getPage()).startCheckout();
})

Then('I will fill in my information', async () => {
await new Checkout(getPage()).fillInCheckout();
})

Then('I will checkout', async () => {
await new Checkout(getPage()).checkoutItems();
})
17 changes: 13 additions & 4 deletions steps/login.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@ import { Then } from '@cucumber/cucumber';
import { getPage } from '../playwrightUtilities';
import { Login } from '../pages/login.page';

Then('I should see the title {string}', async (expectedTitle) => {
Then('I should see the title {string}', async (expectedTitle: string): Promise<void> => {
await new Login(getPage()).validateTitle(expectedTitle);
});

Then('I will login as {string}', async (userName) => {
await new Login(getPage()).loginAsUser(userName);
});
Then('I will login as {string} with this password {string}', async (username: string, password: string): Promise<void> => {
await new Login(getPage()).loginAsUser(username, password);
});

// Without credentials - defaults to standard_user
Then('I will login', async (): Promise<void> => {
await new Login(getPage()).loginAsUser('standard_user', 'secret_sauce');
});

Then('I should see this result {string}', async (expectedErrorMessage: string): Promise<void> => {
await new Login(getPage()).validateErrorMessage(expectedErrorMessage);
})
7 changes: 7 additions & 0 deletions steps/logout.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 { Logout } from '../pages/logout.page';

Then('I logged out of the application', async () => {
await new Logout(getPage()).logout();
});
11 changes: 10 additions & 1 deletion 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 will go to my shopping cart', async () => {
await new Product(getPage()).selectCart();
});

Then('I will sort by {string}, and the results should be {string}', async (sort: string, prices: string) => {
await new Product(getPage()).selectDropdown(sort);
await new Product(getPage()).sortItems(prices)
})
Loading