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: 3 additions & 2 deletions features/login.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ 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 container
Then The error message container should contain the text "Epic sadface: Sorry, this user has been locked out."
26 changes: 18 additions & 8 deletions features/product.feature
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,22 @@ Feature: Product Feature

Background:
Given I open the "https://www.saucedemo.com/" page
Scenario Outline: Validate product sort by price
Then I will login as 'standard_user'
And I will sort items by '<price sort>'
Then The items should be sorted by price '<price sort>'

# 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 |
Examples:
| price sort |
| Price (low to high) |
| Price (high to low) |

Scenario Outline: Validate product sort by name
Then I will login as 'standard_user'
And I will sort items by '<name sort>'
Then The items should be sorted by name '<name sort>'

Examples:
| name sort |
| Name (A to Z) |
| Name (Z to A) |
37 changes: 33 additions & 4 deletions features/purchase.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,41 @@ 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
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)
Then I will click the cart button
# TODO: Select Checkout
Then I will click the checkout button
# TODO: Fill in the First Name, Last Name, and Zip/Postal Code
Then I will fill the checkout form as "Test" "User", "12345"
# TODO: Select Continue
Then I will click the continue button
# TODO: Select Finish
# TODO: Validate the text 'Thank you for your order!'
Then I will click the finish button
# TODO: Validate the text 'Thank you for your order!'
Then The checkout success container should be visible
And The checkout success header should read 'Thank you for your order!'
And The checkout success text should read 'Your order has been dispatched, and will arrive just as fast as the pony can get there!'

Scenario: Add item to cart
Then I will login as 'standard_user'
Then I will add "Sauce Labs Backpack" to the cart
Then The shopping cart badge count should be 1
And I will add "Sauce Labs Fleece Jacket" to the cart
Then The shopping cart badge count should be 2
Then I will click the cart button
Then The cart should contain "Sauce Labs Backpack"
And The cart should contain "Sauce Labs Fleece Jacket"

Scenario: Remove item from cart
Then I will login as 'standard_user'
Then I will add "Sauce Labs Backpack" to the cart
And I will add "Sauce Labs Fleece Jacket" to the cart
Then The shopping cart badge count should be 2
Then I will remove "Sauce Labs Backpack" from the cart
Then The shopping cart badge count should be 1
Then I will click the cart button
Then The cart should contain "Sauce Labs Fleece Jacket"
And The cart should not contain "Sauce Labs Backpack"
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.

11 changes: 10 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 { expect, Page } 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 errorContainer: string = 'div.error-message-container.error'

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

public async validateErrorExists() {
await expect(this.page.locator(this.errorContainer)).toBeVisible();
}

public async validateErrorMessage(expectedMessage: string) {
await expect(this.page.locator(this.errorContainer)).toContainText(expectedMessage);
}
}
72 changes: 64 additions & 8 deletions pages/product.page.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,70 @@
import { Page } from "@playwright/test"
import { expect, 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 page: Page;
private readonly sortDropdown: string =
'[data-test="product-sort-container"]';
private readonly itemContainer: string = '[data-test="inventory-list"] > div';
private readonly itemPrice: string = `[data-test="inventory-item-price"]`;
private readonly itemName: string = `[data-test^="item-"][data-test$="-title-link"]`;

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

public async selectSortOption(sort: string) {
await this.page.locator(this.sortDropdown).selectOption({ label: sort });
}

public async assertPricesSorted(sort: string) {
// Get all prices
const prices: number[] = [];
for (
let i = 0;
i <
(await this.page
.locator(`${this.itemContainer} ${this.itemPrice}`)
.count());
i++
) {
const priceFormatted = parseFloat(
(
await this.page
.locator(`${this.itemContainer} ${this.itemPrice}`)
.nth(i)
.innerText()
).replace("$", ""),
);
prices.push(priceFormatted);
}
console.log(`prices:`, prices);
const pricesSorted = [...prices].sort((a, b) =>
sort === "Price (low to high)" ? a - b : b - a,
);
expect(prices).toEqual(pricesSorted);
}

public async addBackPackToCart() {
await this.page.locator(this.addToCart).click()
public async assertNamesSorted(sort: string) {
// Get all prices
const names: string[] = [];
for (
let i = 0;
i <
(await this.page
.locator(`${this.itemContainer} ${this.itemName}`)
.count());
i++
) {
const name = await this.page
.locator(`${this.itemContainer} ${this.itemName}`)
.nth(i)
.innerText();
names.push(name);
}
}
console.log(`names:`, names);
const namesSorted = [...names].sort((a, b) =>
sort === "Name (A to Z)" ? a.localeCompare(b) : b.localeCompare(a),
);
expect(names).toEqual(namesSorted);
}
}
114 changes: 114 additions & 0 deletions pages/purchase.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { expect, Page } from "@playwright/test";

export class Purchase {
private readonly page: Page;
private readonly addToCart: string =
'button[id="add-to-cart-sauce-labs-backpack"]';
private readonly cartButton: string = '[data-test="shopping-cart-link"]';
private readonly cartButtonBadge: string =
'[data-test="shopping-cart-badge"]';
private readonly checkoutButton: string = '[data-test="checkout"]';
private readonly checkoutForm = {
firstNameInput: '[data-test="firstName"]',
lastNameInput: '[data-test="lastName"]',
zipCodeInput: '[data-test="postalCode"]',
};
private readonly continueButton: string = '[data-test="continue"]';
private readonly finishButton: string = '[data-test="finish"]';
private readonly checkoutCompleteContainer: string =
'[data-test="checkout-complete-container"]';
private readonly checkoutCompleteHeader: string =
'[data-test="complete-header"]';
private readonly checkoutCompleteText: string = '[data-test="complete-text"]';
private readonly itemContainer: string = '[data-test="inventory-list"] > div';
private readonly addToCartGeneric: string = 'button[id^="add-to-cart-"]';
private readonly removeFromCartGeneric: string = 'button[id^="remove-"]';
private readonly cartItem: string =
'[data-test="cart-list"] [data-test="inventory-item"]';

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

public async addBackPackToCart() {
await this.page.locator(this.addToCart).click();
}

public async clickCartButton() {
await this.page.locator(this.cartButton).click();
}

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

public async fillCheckoutForm(
firstName: string,
lastName: string,
zipCode: string,
) {
await this.page.locator(this.checkoutForm.firstNameInput).fill(firstName);
await this.page.locator(this.checkoutForm.lastNameInput).fill(lastName);
await this.page.locator(this.checkoutForm.zipCodeInput).fill(zipCode);
}

public async clickContinueButton() {
await this.page.locator(this.continueButton).click();
}

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

public async assertSuccessContainerVisible() {
await expect(
this.page.locator(this.checkoutCompleteContainer),
).toBeVisible();
}

public async assertSuccessHeaderCorrect(expectedText: string) {
await expect(this.page.locator(this.checkoutCompleteHeader)).toHaveText(
expectedText,
);
}

public async assertSuccessTextCorrect(expectedText: string) {
await expect(this.page.locator(this.checkoutCompleteText)).toHaveText(
expectedText,
);
}

public async addItemToShoppingCart(itemName: string) {
await this.page
.locator(this.itemContainer)
.filter({ hasText: itemName })
.locator(this.addToCartGeneric)
.click();
}

public async assertShoppingCartBadgeCount(expectedCount: number) {
await expect(
this.page.locator(`${this.cartButton} ${this.cartButtonBadge}`),
).toHaveText(`${expectedCount}`);
}

public async assertItemIsVisibleInCart(itemName: string) {
await expect(
this.page.locator(this.cartItem).filter({ hasText: itemName }),
).toBeVisible();
}

public async removeItemFromCart(itemName: string) {
await this.page
.locator(this.itemContainer)
.filter({ hasText: itemName })
.locator(this.removeFromCartGeneric)
.click();
}

public async assertItemIsNotVisibleInCart(itemName: string) {
await expect(
this.page.locator(this.cartItem).filter({ hasText: itemName }),
).toHaveCount(0);
}
}
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 container', async () => {
await new Login(getPage()).validateErrorExists();
});

Then('The error message container should contain the text {string}', async (expectedMessage) => {
await new Login(getPage()).validateErrorMessage(expectedMessage);
});
21 changes: 15 additions & 6 deletions steps/product.steps.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { Then } from '@cucumber/cucumber';
import { getPage } from '../playwrightUtilities';
import { Product } from '../pages/product.page';
import { Product } from './../pages/product.page';
import { Then } from "@cucumber/cucumber";
import { getPage } from "../playwrightUtilities";

Then("I will sort items by {string}", async (sortOption: string) => {
await new Product(getPage()).selectSortOption(sortOption);
})

Then("The items should be sorted by price {string}", async (sortOption: string) => {
await new Product(getPage()).assertPricesSorted(sortOption);
})

Then("The items should be sorted by name {string}", async (sortOption: string) => {
await new Product(getPage()).assertNamesSorted(sortOption);
})

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