From 16d8a8f2a06ff49e907920d5a5470ef023687288 Mon Sep 17 00:00:00 2001 From: Timothy Cope Date: Wed, 20 Mar 2024 18:09:18 -0700 Subject: [PATCH] working test --- tests/fixtures/login.fixture.ts | 2 +- tests/fixtures/shoppingCart.fixture.ts | 10 +++ tests/pages/login.page.ts | 6 +- tests/pages/shoppingCart.page.ts | 85 ++++++++++++++++++++++++++ tests/shoppingCart.spec.ts | 17 ++++++ 5 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 tests/fixtures/shoppingCart.fixture.ts create mode 100644 tests/pages/shoppingCart.page.ts create mode 100644 tests/shoppingCart.spec.ts diff --git a/tests/fixtures/login.fixture.ts b/tests/fixtures/login.fixture.ts index ad1f4e7..2914aec 100644 --- a/tests/fixtures/login.fixture.ts +++ b/tests/fixtures/login.fixture.ts @@ -3,7 +3,7 @@ import LoginPage from "../pages/login.page"; /** Extend the base page with the login page object. */ export const test = base.extend<{loginPage: LoginPage;}>({ - loginPage: async ({ page }, use) => { + loginPage: async ({ page }, use) => { await use(new LoginPage(page)); } }); diff --git a/tests/fixtures/shoppingCart.fixture.ts b/tests/fixtures/shoppingCart.fixture.ts new file mode 100644 index 0000000..41d0a8c --- /dev/null +++ b/tests/fixtures/shoppingCart.fixture.ts @@ -0,0 +1,10 @@ +import { test as base } from "@playwright/test"; +import ShoppingCartPage from "../pages/shoppingCart.page"; + +/** Extend the base page with the shopping cart page object. */ +export const test = base.extend<{ shoppingCartPage: ShoppingCartPage; }>({ + shoppingCartPage: async ({ page }, use) => { + // Use the shopping cart page object + await use(new ShoppingCartPage(page)); + } +}); diff --git a/tests/pages/login.page.ts b/tests/pages/login.page.ts index 47d4993..1b80035 100644 --- a/tests/pages/login.page.ts +++ b/tests/pages/login.page.ts @@ -2,7 +2,7 @@ import type { Page } from '@playwright/test'; import { baseUrl, testUser, testPass } from '../../test-data'; /** URL looks like: {baseURL}/login */ -export default class LoginPage{ +export default class LoginPage { /** The `Page` object to use. */ private page: Page; @@ -17,9 +17,7 @@ export default class LoginPage{ /** The `button` to submit a form. */ buttonSubmit = () => this.page.locator('button[type="submit"]'); - /** Opens `this` page. - * @param page The Fixture to use - */ + /** Opens `this` page. */ public async open() { await this.page.goto(baseUrl + '/login'); } diff --git a/tests/pages/shoppingCart.page.ts b/tests/pages/shoppingCart.page.ts new file mode 100644 index 0000000..713609a --- /dev/null +++ b/tests/pages/shoppingCart.page.ts @@ -0,0 +1,85 @@ +import { expect, Page } from '@playwright/test'; + +export default class ShoppingCart { + + /** The `Page` object to use. */ + private page: Page; + public dialogMessage: string = ""; + constructor(page: Page) { + this.page = page; + } + + /** The `span` for the large size */ + spanLarge = () => this.page.locator('span.checkmark').getByText('L', { exact: true }); + /** The `div` for the "Grey T-shirt" */ + divGreyTshirt = () => this.page.locator('p').filter({ hasText: 'Grey T-shirt' }).locator('..'); + /** The `p` for the "Grey T-shirt" price */ + pGreyTshirtPrice = () => this.divGreyTshirt().locator('p').filter({ hasText: '$' }).first() + /** The `button` to add the "Grey T-shirt" to the cart */ + buttonAddToCart = () => this.divGreyTshirt().locator('button'); + /** The `div` that contains the first shopping cart item */ + divCartItem = () => this.page.getByTitle('remove product from cart').locator('..'); + /** The `p` that contains the first shopping cart item's name */ + pCartItemName = () => this.divCartItem().locator('div > p').first(); + /** The `p` that contains the first shopping cart item's price */ + pCartItemPrice = () => this.divCartItem().locator('p').filter({ hasText: '$' }).first(); + /** The `p` that contains the first shopping cart item's price */ + pCartItemQuantity = () => this.divCartItem().locator('p').filter({ hasText: 'Quantity:' }).first(); + /** The `button` to checkout */ + buttonCheckout = () => this.page.locator('button').filter({ hasText: 'Checkout' }); + /** The `div` that contains the subtotal */ + divSubtotal = () => this.buttonCheckout().locator('..'); + /** The `p` for the subtotal price */ + pSubTotalPrice = () => this.divSubtotal().locator('p').filter({ hasText: '$' }).first() + + /** Opens `this` page. */ + public async open() { + + // 1. Typescript React Shopping cart (react-shopping-cart-67954.firebaseapp.com) + await this.page.goto('https://react-shopping-cart-67954.firebaseapp.com/'); + + } + + /** Adds an item to cart. */ + public async addItemToCart() { + + // 2. Select size "L". + await this.spanLarge().click(); + + // 3. Note the price of a "Grey T-shirt" card. + let price = (await this.pGreyTshirtPrice().innerText()).replace(' ', ''); + + // 4. Click "Add to a cart" for "Grey T-shirt". + await this.buttonAddToCart().click(); + + // 5. Check that "Cart" drawer appears. + let visible = await this.divCartItem().isVisible(); + expect(visible).toBeTruthy(); + + // 6. Check that selected item appears, it has correct name, size, description, quantity, and price matches to step 3. + // NOTE: Only the name, price, and quantity are available here. + let name = await this.pCartItemName().innerText(); + expect(name).toBe('Grey T-shirt'); + let cost = (await this.pCartItemPrice().innerText()).replace(' ', ''); + expect(cost).toBe('$14.90'); + let quantity = (await this.pCartItemQuantity().innerText()).split(" ").pop(); + expect(quantity).toBe('1'); + + // 7. Check that "Subtotal" matches to step 3. + let subtotal = (await this.pSubTotalPrice().innerText()).replace(' ', ''); + expect(subtotal).toBe(price); + + // 8. Click "Checkout" and check that the popup appears. + const dialogPromise = new Promise(resolve => { + this.page.on('dialog', async dialog => { + this.dialogMessage = dialog.message(); + await dialog.dismiss(); + resolve(this.dialogMessage); + }); + }); + await this.buttonCheckout().click(); + await dialogPromise; + + } + +} diff --git a/tests/shoppingCart.spec.ts b/tests/shoppingCart.spec.ts new file mode 100644 index 0000000..46dc3da --- /dev/null +++ b/tests/shoppingCart.spec.ts @@ -0,0 +1,17 @@ +// Feature: Shopping Cart Flow +import { expect } from '@playwright/test'; +import { test } from './fixtures/shoppingCart.fixture'; + +// Scenario: As a user, I can add items to my shopping cart and checkout +test('add items to shopping cart', async ({ shoppingCartPage }) => { + + // [Arrange] Given I am on the shopping cart page + await shoppingCartPage.open(); + + // [Act] When I add an item to the cart and select checkout + await shoppingCartPage.addItemToCart(); + + // [Assert] Then I should see a popup matching my selection + expect(shoppingCartPage.dialogMessage).toBe('Checkout - Subtotal: $ 14.90'); + +});