From 342d0e4a69d5a2d4df151cdf5e7d2bcb953ce2e7 Mon Sep 17 00:00:00 2001 From: Andrew Shapiro Date: Mon, 23 Mar 2026 14:38:33 -0400 Subject: [PATCH 1/2] Finalized automation assessment with README and report --- .gitignore | 3 +- README.md | 70 +- cucumber_report.html | 1895 +++++++++++++++++++++++++++++++++++++ features/login.feature | 5 +- features/product.feature | 11 +- features/purchase.feature | 15 +- package-lock.json | 2 +- pages/login.page.ts | 6 +- pages/product.page.ts | 73 +- steps/login.steps.ts | 4 + steps/product.steps.ts | 39 +- 11 files changed, 2089 insertions(+), 34 deletions(-) create mode 100644 cucumber_report.html diff --git a/.gitignore b/.gitignore index 9fd6689..fb209d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ report.json -cucumber_report.html -/node_modules \ No newline at end of file +node_modules/ \ No newline at end of file diff --git a/README.md b/README.md index b911105..f1ba667 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,20 @@ # Sample Playwright Automation Test +This project demonstrates UI test automation using Playwright, Cucumber (BDD), and TypeScript. + ## System Requirements node >= v18.5.x npm >= v7 - ## Setup // Install Visual Studio Code (or any editor) - https://code.visualstudio.com/download // Install Node.js - https://nodejs.org/en/download @@ -32,7 +31,7 @@ Cucumber v1.7.0 Cucumber (Gherkin) Support enhanced for Behat -## Instructions +### Instructions To run the test ```bash npm run test @@ -44,15 +43,62 @@ npm run report ``` It is not expected that you complete every task, however, please give your best effort - You will be scored based on your ability to complete the following tasks: -- [ ] Install and setup this repository on your personal computer -- [ ] Complete the automation tasks listed below +- [X] Install and setup this repository on your personal computer +- [X] Complete the automation tasks listed below ### Tasks -- [ ] Modify the scenario 'Validate the login page title' from [login.feature](features/login.feature#8) which runs but fails. Determine the cause of the failure and update the scenario to pass in the test -- [ ] Extend the scenario 'Validate login error message' from [login.feature](features/login.feature#10) which runs and passes but is missing a step. Extend the scenario to validate the error message received. -- [ ] Modify and extend the 'Validate successful purchase text' from [purchase.feature](features/purchase.feature#6) with steps for each comment listed. Consider writing a new steps.ts file along with an appropriate page.ts -- [ ] Modify and extend the 'Validate product sort by price sort' from [product.feature](features/product.feature#6) with steps for each comment listed. Utilize the Scenario Outline and Examples table to parameterize the test -- [ ] Extend the testing coverage with anything you believe would be beneficial +- [X] Modify the scenario 'Validate the login page title' from [login.feature](features/login.feature#8) which runs but fails. Determine the cause of the failure and update the scenario to pass in the test +- [X] Extend the scenario 'Validate login error message' from [login.feature](features/login.feature#10) which runs and passes but is missing a step. Extend the scenario to validate the error message received. +- [X] Modify and extend the 'Validate successful purchase text' from [purchase.feature](features/purchase.feature#6) with steps for each comment listed. Consider writing a new steps.ts file along with an appropriate page.ts +- [X] Modify and extend the 'Validate product sort by price sort' from [product.feature](features/product.feature#6) with steps for each comment listed. Utilize the Scenario Outline and Examples table to parameterize the test +- [X] Extend the testing coverage with anything you believe would be beneficial + +#### Solution Implementation +The following tasks were completed and extended: + +Login +- Fixed login page title validation +- Implemented locked-out user error validation + +##### Purchase Flow (End-to-End) +-Add product to cart +-Navigate to cart +-Proceed to checkout +-Fill checkout information +-Complete purchase +-Validate success message + +###### Product Sorting +-Implemented sorting validation using Scenario Outline +-Supported: + Price (low to high) + Price (high to low) +-Extracted prices dynamically from UI +-Compared actual values with sorted results + +###### Additional Test Coverage +- Cart icon item count validation + +###### Framework Design + +The project follows automation best practices: +-Page Object Model (POM) +-Cucumber BDD structure +-Separation of concerns: +-Feature files → business scenarios +-Step definitions → test logic +-Page objects → UI interactions +-Reusable methods and stable selectors (id, data-test) are used to improve maintainability. + +###### Test Report +An HTML test report is included: +cucumber_report.html +The report can be opened directly in a browser. +###### Notes +Sorting validation is implemented dynamically (no hardcoded values) +Stable selectors (id, data-test) are used where applicable +The framework is designed to be scalable and maintainable +###### Summary +This project demonstrates an end-to-end automation solution with structured test design, reusable components, and extended test coverage. \ No newline at end of file diff --git a/cucumber_report.html b/cucumber_report.html new file mode 100644 index 0000000..e9ff150 --- /dev/null +++ b/cucumber_report.html @@ -0,0 +1,1895 @@ + + + + + Cucumber Feature Report + + + + + + + + + + +
+ +
+
+
+

All Scenarios

+
+
+
+ 5 +
+
+
+
+
+

Passed Scenarios

+
+
+
+ 5 +
+
+
+
+
+

Failed Scenarios

+
+
+
+ 0 +
+
+
+ + + + + +
+
+
+
+
+ +
+

+ Expand All + | + Collapse All +

+
+ + +
+ +
+
+
+ + +
+
+
+
+ + + + + +
+ + +
+ +
+
+ +
+
+ + + +
+ +
+
+
+ + + + + +

+

+ + + + + + + Given + I open the "https://www.saucedemo.com/" page + + + + 552ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I should see the title "Swag Labs" + + + + 9ms + + + + + + + + + + + + + + + + + + +
+

+ + + + +
+
+
+ + +
+ +
+
+
+ + + + + +

+

+ + + + + + + Given + I open the "https://www.saucedemo.com/" page + + + + 391ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I will login as 'locked_out_user' + + + + 143ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I should see the error message "Epic sadface: Sorry, this user has been locked out." + + + + 10ms + + + + + + + + + + + + + + + + + + +
+

+ + + + +
+
+
+ +
+
+
+
+ +
+ + + + +
+ +
+
+ +
+
+ + + +
+ +
+
+
+ + + + + +

+

+ + + + + + + Given + I open the "https://www.saucedemo.com/" page + + + + 336ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I will login as "standard_user" + + + + 172ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I will sort the items by "Price (low to high)" + + + + 39ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I should see all items are sorted correctly by price "Price (low to high)" + + + + 17ms + + + + + + + + + + + + + + + + + + +
+

+ + + + +
+
+
+ + +
+ +
+
+
+ + + + + +

+

+ + + + + + + Given + I open the "https://www.saucedemo.com/" page + + + + 379ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I will login as "standard_user" + + + + 191ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I will sort the items by "Price (high to low)" + + + + 28ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I should see all items are sorted correctly by price "Price (high to low)" + + + + 14ms + + + + + + + + + + + + + + + + + + +
+

+ + + + +
+
+
+ +
+
+
+
+ +
+ +
+ + + +
+ + +
+ +
+
+ +
+
+ + + +
+ +
+
+
+ + + + + +

+

+ + + + + + + Given + I open the "https://www.saucedemo.com/" page + + + + 381ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I will login as "standard_user" + + + + 199ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I will add the backpack to the cart + + + + 48ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I should see cart badge with "1" item + + + + 6ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I will go to the cart + + + + 85ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I will proceed to Checkout + + + + 55ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I will Fill in Checkout information + + + + 42ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I will Continue to Checkout + + + + 54ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I will Finish the purchase + + + + 61ms + + + + + + + + + + + + + + + + + + +
+

+ + + +

+

+ + + + + + + Then + I should see successful message "Thank you for your order!" + + + + 3ms + + + + + + + + + + + + + + + + + + +
+

+ + + + +
+
+
+ +
+
+
+
+ +
+ +
+ + + + +
+ + + + + + + + + + + + diff --git a/features/login.feature b/features/login.feature index fb9f1fa..654c853 100644 --- a/features/login.feature +++ b/features/login.feature @@ -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 \ No newline at end of file + Then I should see the error message "Epic sadface: Sorry, this user has been locked out." \ No newline at end of file diff --git a/features/product.feature b/features/product.feature index 8a7ceab..0bbf1a7 100644 --- a/features/product.feature +++ b/features/product.feature @@ -5,9 +5,10 @@ Feature: Product Feature # 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 - Then I will login as 'standard_user' - # TODO: Sort the items by - # TODO: Validate all 6 items are sorted correctly by price + Then I will login as "standard_user" + Then I will sort the items by "" + Then I should see all items are sorted correctly by price "" Examples: - # TODO: extend the datatable to paramterize this test - | sort | \ No newline at end of file + | sort | + | Price (low to high) | + | Price (high to low) | \ No newline at end of file diff --git a/features/purchase.feature b/features/purchase.feature index 2863478..f4bfb7b 100644 --- a/features/purchase.feature +++ b/features/purchase.feature @@ -4,11 +4,12 @@ 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 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!' \ No newline at end of file + Then I should see cart icon with "1" item + Then I will go to the cart + Then I will proceed to Checkout + Then I will Fill in Checkout information + Then I will Continue to Checkout + Then I will Finish the purchase + Then I should see successful message "Thank you for your order!" \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b90d7c6..b5c47aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "Playwright-Project", + "name": "Playwright-Cucumber-Exercise", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/pages/login.page.ts b/pages/login.page.ts index 5a01614..3b4f039 100644 --- a/pages/login.page.ts +++ b/pages/login.page.ts @@ -1,4 +1,4 @@ -import { Page } from "@playwright/test" +import { expect, Page } from "@playwright/test" export class Login { private readonly page: Page @@ -23,4 +23,8 @@ 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 errorMessage = this.page.locator('[data-test="error"]'); + await expect(errorMessage).toHaveText(expectedMessage); + } } \ No newline at end of file diff --git a/pages/product.page.ts b/pages/product.page.ts index 14bedb1..711c1ab 100644 --- a/pages/product.page.ts +++ b/pages/product.page.ts @@ -1,14 +1,83 @@ -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 cartButton: string = 'a[data-test="shopping-cart-link"]' + private readonly checkoutButton: string = 'button[id="checkout"]' + private readonly firstNameField: string = 'input[id="first-name"]' + private readonly lastNameField: string = 'input[id="last-name"]' + private readonly postalCodeField: string = 'input[id="postal-code"]' + private readonly continueButton: string = 'input[id="continue"]' + private readonly finishButton: string = 'button[id="finish"]' + private readonly successMessage: string = 'h2[data-test="complete-header"]' + + private readonly cartIcon: string = '[data-test="shopping-cart-badge"]' + + private readonly dropdownSort: string = 'select[data-test="product-sort-container"]' + private readonly itemPrices: string = '[data-test="inventory-item-price"]' + + private readonly firstNameValue: string = 'John' + private readonly lastNameValue: string = 'Smith' + private readonly postalCodeValue: string = '121212' 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 validateCartIcon(expectedCount: string) { + await expect(this.page.locator(this.cartIcon)).toHaveText(expectedCount); + } + + + public async goToCart() { + await this.page.locator(this.cartButton).click(); + } + + public async proceedToCheckout() { + await this.page.locator(this.checkoutButton).click(); + } + + public async fillCheckoutInformation() { + await this.page.locator(this.firstNameField).fill(this.firstNameValue) + await this.page.locator(this.lastNameField).fill(this.lastNameValue) + await this.page.locator(this.postalCodeField).fill(this.postalCodeValue); + } + + public async continueToCheckout() { + await this.page.locator(this.continueButton).click(); + } + + public async finishThePurchase() { + await this.page.locator(this.finishButton).click(); + } + + public async validateSuccessMessage(expectedMessage: string) { + await expect(this.page.locator(this.successMessage)).toHaveText(expectedMessage); + + } + + public async sortItemsByPrice(sortOption: string) { + await this.page.locator(this.dropdownSort).selectOption({ label: sortOption }); + } + + public async validateItemsSortedByPrice(sortOption: string) { + const priceValue = await this.page.locator(this.itemPrices).allTextContents(); + + const prices = priceValue.map(p => Number(p.replace('$', '').trim())); + + const sortedPrices = [...prices].sort((a, b) => a - b); + + if (sortOption === 'Price (low to high)') { + expect(prices).toEqual(sortedPrices); + } else if (sortOption === 'Price (high to low)') { + expect(prices).toEqual([...sortedPrices].reverse()); + } else { + throw new Error(`Unsupported sorting option: ${sortOption}`); + } } } \ No newline at end of file diff --git a/steps/login.steps.ts b/steps/login.steps.ts index c2aa0d8..141e3f2 100644 --- a/steps/login.steps.ts +++ b/steps/login.steps.ts @@ -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 the error message {string}', async (message: string) => { + await new Login(getPage()).validateErrorMessage(message); }); \ No newline at end of file diff --git a/steps/product.steps.ts b/steps/product.steps.ts index bb52fb9..cafff9a 100644 --- a/steps/product.steps.ts +++ b/steps/product.steps.ts @@ -4,4 +4,41 @@ import { Product } from '../pages/product.page'; Then('I will add the backpack to the cart', async () => { await new Product(getPage()).addBackPackToCart(); -}); \ No newline at end of file +}); + +Then('I should see cart icon with {string} item', async (count: string) => { +await new Product(getPage()).validateCartIcon(count); +}); + + +Then ('I will go to the cart', async () => { + await new Product(getPage()).goToCart(); +}); + +Then ('I will proceed to Checkout', async () => { + await new Product(getPage()).proceedToCheckout(); +}); + +Then ('I will Fill in Checkout information', async () => { + await new Product(getPage()).fillCheckoutInformation(); +}); + +Then ('I will Continue to Checkout', async () => { + await new Product(getPage()).continueToCheckout(); +}); + +Then ('I will Finish the purchase', async () => { + await new Product(getPage()).finishThePurchase(); +}); + +Then ('I should see successful message {string}', async (message: string) => { + await new Product(getPage()).validateSuccessMessage(message); +}); + +Then('I will sort the items by {string}', async (sortOption: string) => { + await new Product(getPage()).sortItemsByPrice(sortOption); +}); + +Then('I should see all items are sorted correctly by price {string}', async (sortOption: string) => { + await new Product(getPage()).validateItemsSortedByPrice(sortOption); +}); From 8d985f5607b5718b1db7d33e0bf9a51a0d700ed1 Mon Sep 17 00:00:00 2001 From: Andrew Shapiro Date: Mon, 23 Mar 2026 14:47:15 -0400 Subject: [PATCH 2/2] Fix README formating --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f1ba667..9f7659c 100644 --- a/README.md +++ b/README.md @@ -55,14 +55,14 @@ You will be scored based on your ability to complete the following tasks: - [X] Modify and extend the 'Validate product sort by price sort' from [product.feature](features/product.feature#6) with steps for each comment listed. Utilize the Scenario Outline and Examples table to parameterize the test - [X] Extend the testing coverage with anything you believe would be beneficial -#### Solution Implementation +### Solution Implementation The following tasks were completed and extended: Login - Fixed login page title validation - Implemented locked-out user error validation -##### Purchase Flow (End-to-End) +### Purchase Flow (End-to-End) -Add product to cart -Navigate to cart -Proceed to checkout @@ -70,7 +70,7 @@ Login -Complete purchase -Validate success message -###### Product Sorting +### Product Sorting -Implemented sorting validation using Scenario Outline -Supported: Price (low to high) @@ -78,10 +78,10 @@ Login -Extracted prices dynamically from UI -Compared actual values with sorted results -###### Additional Test Coverage +### Additional Test Coverage - Cart icon item count validation -###### Framework Design +### Framework Design The project follows automation best practices: -Page Object Model (POM) @@ -92,13 +92,13 @@ The project follows automation best practices: -Page objects → UI interactions -Reusable methods and stable selectors (id, data-test) are used to improve maintainability. -###### Test Report +### Test Report An HTML test report is included: cucumber_report.html The report can be opened directly in a browser. -###### Notes +### Notes Sorting validation is implemented dynamically (no hardcoded values) Stable selectors (id, data-test) are used where applicable The framework is designed to be scalable and maintainable -###### Summary +### Summary This project demonstrates an end-to-end automation solution with structured test design, reusable components, and extended test coverage. \ No newline at end of file