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 login error message "Epic sadface: Sorry, this user has been locked out."
17 changes: 9 additions & 8 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
Examples:
# TODO: extend the datatable to paramterize this test
| sort |
Scenario Outline: Validate product sort by price <sort>
Then I will login as 'standard_user'
Then I sort the products by "<sort>"
Then the product prices should be sorted "<order>"

Examples:
| sort | order |
| Price (low to high) | ascending |
| Price (high to low) | descending |
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 select the cart
Then I select checkout
Then I enter first name "Victor", last name "Cardauc", and postal code "32541"
Then I select continue
Then I select finish
Then I should see the purchase confirmation "Thank you for your order!"
9 changes: 9 additions & 0 deletions 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,12 @@ 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 error message to be "${expectedMessage}" but found "${actualMessage?.trim()}"`);
}

}
}
115 changes: 111 additions & 4 deletions pages/product.page.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,121 @@
import { Page } from "@playwright/test"
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 page: Page;

private readonly addToCart: string =
'button[id="add-to-cart-sauce-labs-backpack"]';

private readonly cart: string =
'[data-test="shopping-cart-link"]';

private readonly checkoutButton: string =
'[data-test="checkout"]';

private readonly firstNameField: string =
'[data-test="firstName"]';

private readonly lastNameField: string =
'[data-test="lastName"]';

private readonly postalCodeField: string =
'[data-test="postalCode"]';

private readonly continueButton: string =
'[data-test="continue"]';

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

private readonly purchaseConfirmation: string =
'[data-test="complete-header"]';

private readonly sortDropdown: string =
'[data-test="product-sort-container"]';

private readonly productPrices: string =
'[data-test="inventory-item-price"]';

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

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

public async selectCart() {
await this.page.locator(this.cart).click();
}

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

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

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

public async validatePurchaseConfirmation(expectedMessage: string) {
const actualMessage = await this.page
.locator(this.purchaseConfirmation)
.textContent();

if (actualMessage?.trim() !== expectedMessage) {
throw new Error(
`Expected purchase confirmation to be "${expectedMessage}" but found "${actualMessage?.trim()}"`
);
}
}
public async sortProducts(sortOption: string) {
if (sortOption === 'Price (low to high)') {
await this.page.locator(this.sortDropdown).selectOption('lohi');
} else if (sortOption === 'Price (high to low)') {
await this.page.locator(this.sortDropdown).selectOption('hilo');
} else {
throw new Error(`Unsupported sort option: ${sortOption}`);
}
}

public async validateProductPricesSorted(order: string) {
const priceTexts = await this.page
.locator(this.productPrices)
.allTextContents();

const actualPrices = priceTexts.map((price) =>
Number(price.replace('$', '').trim())
);

const expectedPrices = [...actualPrices];

if (order === 'ascending') {
expectedPrices.sort((a, b) => a - b);
} else if (order === 'descending') {
expectedPrices.sort((a, b) => b - a);
} else {
throw new Error(`Unsupported sort order: ${order}`);
}

if (JSON.stringify(actualPrices) !== JSON.stringify(expectedPrices)) {
throw new Error(
`Prices are not sorted ${order}.
Actual: ${actualPrices.join(', ')}
Expected: ${expectedPrices.join(', ')}`
);
}
}
}
4 changes: 4 additions & 0 deletions 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 login error message {string}', async (expectedMessage: string) => {
await new Login(getPage()).validateErrorMessage(expectedMessage);
});
54 changes: 52 additions & 2 deletions steps/product.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,55 @@ 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();
});
await new Product(getPage()).addBackPackToCart();
});

Then('I select the cart', async () => {
await new Product(getPage()).selectCart();
});

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

Then(
'I enter first name {string}, last name {string}, and postal code {string}',
async (
firstName: string,
lastName: string,
postalCode: string
) => {
await new Product(getPage()).enterCustomerInformation(
firstName,
lastName,
postalCode
);
}
);

Then('I select continue', async () => {
await new Product(getPage()).selectContinue();
});

Then('I select finish', async () => {
await new Product(getPage()).selectFinish();
});

Then(
'I should see the purchase confirmation {string}',
async (expectedMessage: string) => {
await new Product(getPage()).validatePurchaseConfirmation(
expectedMessage
);
});

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

Then(
'the product prices should be sorted {string}',
async (order: string) => {
await new Product(getPage()).validateProductPricesSorted(order);
}
);
Loading