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
Binary file added .DS_Store
Binary file not shown.
20 changes: 16 additions & 4 deletions features/login.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,21 @@ 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"
# TODO: Fix this failing scenario- done below
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"
# TODO: Add a step to validate the error message received- done below
Then I should see the error message "Epic sadface: Sorry, this user has been locked out."

# TODO: Extending the testing coverage- done below
Scenario: Validate login with invalid credentials
Then I will login with username "invalid" and password "invalid"
Then I should see the error message "Epic sadface: Username and password do not match any user in this service"

# TODO: Extending the testing coverage- done below
Scenario: Validate user logout
Then I will login as "standard_user"
Then I will logout
Then I should see the login page
19 changes: 12 additions & 7 deletions features/product.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ Feature: Product Feature
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"
# TODO: Sort the items by <sort>- done below
Then I will sort products by "<sort>"
# TODO: Validate all 6 items are sorted correctly by price- done below
Then I should see all items sorted by "<order>"
# TODO: extend the datatable to paramterize this test- done below
Examples:
| sort | order |
| Price (low to high) | asc |
| Price (high to low) | desc |
26 changes: 17 additions & 9 deletions features/purchase.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@ 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
# TODO: Select the cart (top-right)- done below
Then I will open the cart
# TODO: Extending the testing coverage- done below
Then I should see the product "Sauce Labs Backpack" in the cart
# TODO: Select Checkout- done below
Then I will proceed to checkout
# TODO: Fill in the First Name, Last Name, and Zip/Postal Code- done below
Then I will fill in checkout information "Nishi" "Mewada" "28262"
# TODO: Select Continue- done below
Then I will continue to overview
# TODO: Select Finish- done below
Then I will finish the purchase
# TODO: Validate the text "Thank you for your order!"- done below
Then I should see the purchase confirmation text "Thank you for your order!"
24 changes: 24 additions & 0 deletions pages/cart.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { expect, Page } from "@playwright/test"

export class Cart {
private readonly page: Page
private readonly cartIcon: string = '.shopping_cart_link';
private readonly checkoutButton: string = '#checkout';
private readonly cartItems: string = '.cart_item';

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

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

public async validateProductInCart(expectedProduct: string) {
await expect(this.page.locator(this.cartItems)).toContainText(expectedProduct);
}

public async proceedToCheckout() {
await this.page.locator(this.checkoutButton).click();
}
}
36 changes: 36 additions & 0 deletions pages/checkout.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Page } from "@playwright/test"

export class Checkout {
private readonly page: Page
private readonly firstNameField: string = '#first-name';
private readonly lastNameField: string = '#last-name';
private readonly zipField: string = '#postal-code';
private readonly continueButton: string = '#continue';
private readonly finishButton: string = '#finish';
private readonly confirmationText: string = '.complete-header';

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

public async fillInformation(firstName: string, lastName: string, zip: string) {
await this.page.locator(this.firstNameField).fill(firstName);
await this.page.locator(this.lastNameField).fill(lastName);
await this.page.locator(this.zipField).fill(zip);
}

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

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

public async validateConfirmationText(expectedText: string) {
const actualText = await this.page.locator(this.confirmationText).textContent();
if (actualText?.trim() !== expectedText) {
throw new Error(`Expected confirmation text "${expectedText}" but found "${actualText}"`);
}
}
}
18 changes: 17 additions & 1 deletion pages/login.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ 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"]'
private readonly menuButton: string = '#react-burger-menu-btn'
private readonly logoutLink: string = '#logout_sidebar_link'

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

public async loginWithCredentials(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();
}

public async validateErrorMessage(expectedMessage: string) {
const actualMessage = (await this.page.locator(this.errorMessage).textContent())?.trim();
if (actualMessage !== expectedMessage) {
throw new Error(`Expected error message to be ${expectedMessage} but found ${actualMessage}`);
}
}
}
26 changes: 26 additions & 0 deletions pages/menu.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Page } from "@playwright/test"

export class Menu {
private readonly page: Page

private readonly menuButton: string = '#react-burger-menu-btn'
private readonly logoutLink: string = '#logout_sidebar_link'
private readonly loginButton: string = '#login-button'

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

public async logout() {
await this.page.locator(this.menuButton).click();
await this.page.locator(this.logoutLink).click();
}

public async validateLoginPage() {
const isLoginButtonVisible = await this.page.locator(this.loginButton).isVisible();

if (!isLoginButtonVisible) {
throw new Error('Login page is not displayed after logout');
}
}
}
34 changes: 32 additions & 2 deletions pages/product.page.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
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 addToCart: string = 'button[id="add-to-cart-sauce-labs-backpack"]'
private readonly sortDropdown: string = 'select[data-test="product-sort-container"]'
private readonly itemPrices: string = '.inventory_item_price'

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

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

public async sortByPrice(sortOption: string) {
const valueMap: Record<string, string> = {
'Price (low to high)': 'lohi',
'Price (high to low)': 'hilo',
};
const optionValue = valueMap[sortOption];
if (!optionValue) {
throw new Error(`Unsupported sort option: ${sortOption}`);
}
await this.page.locator(this.sortDropdown).selectOption(optionValue);
}

public async validateSortedByPrice(order: 'asc' | 'desc') {
const priceTexts = await this.page
.locator(this.itemPrices)
.allTextContents();
const prices = priceTexts.map((price) =>
parseFloat(price.replace('$', ''))
);
expect(prices).toHaveLength(6)
const expected = [...prices].sort((a, b) =>
order === 'asc' ? a - b : b - a
);
expect(prices).toEqual(expected);
}
}
3 changes: 2 additions & 1 deletion playwrightUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const DEFAULT_TIMEOUT = 30000;

export const initializeBrowser = async () => {
if (!browser) {
browser = await chromium.launch({ headless: false });
browser = await chromium.launch({ headless: false, channel:"chrome" });
}
};

Expand All @@ -17,6 +17,7 @@ export const initializePage = async () => {
}
};


export const getPage = (): Page => {
if (!page) {
throw new Error('Page has not been initialized. Please call initializePage first.');
Expand Down
32 changes: 32 additions & 0 deletions steps/checkout.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 { Cart } from '../pages/cart.page';
import { Checkout } from '../pages/checkout.page';

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

Then('I should see the product {string} in the cart', async (productName) => {
await new Cart(getPage()).validateProductInCart(productName);
});

Then('I will proceed to checkout', async () => {
await new Cart(getPage()).proceedToCheckout();
});

Then('I will fill in checkout information {string} {string} {string}', async (firstName, lastName, zip) => {
await new Checkout(getPage()).fillInformation(firstName, lastName, zip);
});

Then('I will continue to overview', async () => {
await new Checkout(getPage()).continueToOverview();
});

Then('I will finish the purchase', async () => {
await new Checkout(getPage()).finishPurchase();
});

Then('I should see the purchase confirmation text {string}', async (expectedText) => {
await new Checkout(getPage()).validateConfirmationText(expectedText);
});
17 changes: 17 additions & 0 deletions steps/login.steps.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
import { Then } from '@cucumber/cucumber';
import { getPage } from '../playwrightUtilities';
import { Login } from '../pages/login.page';
import { Menu } from '../pages/menu.page';

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

Then('I will login as {string}', async (userName) => {
await new Login(getPage()).loginAsUser(userName);
});

Then('I will login with username {string} and password {string}', async (userName, password) => {
await new Login(getPage()).loginWithCredentials(userName, password);
});

Then('I should see the error message {string}', async (expectedMessage) => {
await new Login(getPage()).validateErrorMessage(expectedMessage);
});

Then('I will logout', async () => {
await new Menu(getPage()).logout();
});

Then('I should see the login page', async () => {
await new Menu(getPage()).validateLoginPage();
});
10 changes: 9 additions & 1 deletion steps/product.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,12 @@ import { Product } from '../pages/product.page';

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

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

Then('I should see all items sorted by {string}', async (order) => {
await new Product(getPage()).validateSortedByPrice(order as 'asc' | 'desc');
});
Loading