From ee5d858a6939ff2827b8fc2b1751b435a16c2d6b Mon Sep 17 00:00:00 2001 From: bako369 Date: Fri, 7 Aug 2026 14:26:39 -0500 Subject: [PATCH 1/2] Add purchase flow tests and update login/product steps Introduces purchase.feature/page/steps, expands login and product step definitions, and adds a CHANGELOG. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 32 ++++++++++++++++ ClineFlow.md | 78 +++++++++++++++++++++++++++++++++++++++ cucumber.js | 2 +- features/login.feature | 4 +- features/product.feature | 18 +++++---- features/purchase.feature | 8 +++- package-lock.json | 9 ++++- pages/login.page.ts | 25 +++++++++++-- pages/product.page.ts | 39 +++++++++++++++++--- pages/purchase.page.ts | 46 +++++++++++++++++++++++ steps/login.steps.ts | 4 ++ steps/product.steps.ts | 9 +++++ steps/purchase.steps.ts | 27 ++++++++++++++ 13 files changed, 282 insertions(+), 19 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 ClineFlow.md create mode 100644 pages/purchase.page.ts create mode 100644 steps/purchase.steps.ts diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..f977c31e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,32 @@ +# Changelog + +## 2026-08-07 (2) + +### Fixed +- **`pages/product.page.ts`**: Updated the sort-dropdown selector from `select[data-test="product_sort_container"]` to `select[data-test="product-sort-container"]` to match saucedemo.com's current markup (attribute was renamed with a hyphen), which was causing `sortBy` to time out waiting for the dropdown. +- **`steps/product.steps.ts`**: Fixed the price-order normalization in the `I validate items are sorted by price {string}` step — `"Price (high to low)"` was incorrectly matching the `.includes('low')` check and being treated as ascending order, silently mis-validating the descending-sort scenario. +- **`cucumber.js`**: Fixed a missing closing quote after `./hooks/**/*.ts` in the `default` profile string, which caused the `--format` flag to be swallowed into the require glob and broke `npm test` with a "Cucumber instance isn't running" error. + +All Cucumber/Playwright tests now pass (5 scenarios, 22 steps) via `npm test`. + +## 2026-08-07 + +### Added +- **Product Feature**: Implemented sorting by price with new step definitions `I sort items by {string}` and `I validate items are sorted by price {string}`. +- Added `sortBy` and `validateSorted` methods to `pages/product.page.ts` to interact with the sort dropdown and verify price order. +- Updated `features/product.feature` to use a Scenario Outline with examples for low‑to‑high and high‑to‑low sorting. +- Updated `steps/product.steps.ts` with corresponding step implementations. + +### Existing additions (previous) +- **Step Definition**: `I should see error message {string}` in `steps/login.steps.ts` to validate login error messages. +- **Page Method**: `validateErrorMessage(expectedMessage: string)` in `pages/login.page.ts` for checking error visibility and content. +- **Feature Update**: Updated `features/login.feature` scenario *Validate login error message* with the new step to assert the specific error text. + +All Cucumber/Playwright tests now pass (3 scenarios, 8 steps). + +### Added +- **Step Definition**: `I should see error message {string}` in `steps/login.steps.ts` to validate login error messages. +- **Page Method**: `validateErrorMessage(expectedMessage: string)` in `pages/login.page.ts` for checking error visibility and content. +- **Feature Update**: Updated `features/login.feature` scenario *Validate login error message* with the new step to assert the specific error text. + +All Cucumber/Playwright tests now pass (3 scenarios, 8 steps). \ No newline at end of file diff --git a/ClineFlow.md b/ClineFlow.md new file mode 100644 index 00000000..7cb1faa8 --- /dev/null +++ b/ClineFlow.md @@ -0,0 +1,78 @@ +# Cline Flow Guide for Playwright‑Cucumber Exercise + +## Project Structure Overview +``` +Playwright-Cucumber-Exercise/ +│ playwrightUtilities.ts # Helper functions to initialise/close the browser and expose a shared Page +│ ClineFlow.md # <-- **This file** – instructions for the Cline AI agent +│ +├─ pages/ # Page‑object classes (encapsulate locators & actions) +│ ├─ login.page.ts +│ └─ product.page.ts +│ +├─ steps/ # Cucumber step definitions – glue code that drives the tests +│ ├─ common.steps.ts +│ ├─ login.steps.ts +│ └─ product.steps.ts +│ +└─ ... (configuration, feature files, etc.) +``` + +### Primary Tools Present +| Tool/module | Purpose | +|------------|----------| +| **playwrightUtilities.ts** | Provides `initializeBrowser`, `initializePage`, `getPage`, and `closeBrowser`. These are the only entry points for managing the Playwright `Browser` and `Page` instances across the project. | +| **Page objects (`pages/*.ts`)** | Each class holds private locator strings (e.g. `userNameField`, `addToCart`) and public methods that perform actions using `this.page.locator(...)`. | +| **Step definitions (`steps/*.ts`)** | Cucumber `Given`, `Then`, etc. that import the utilities and page objects, instantiate the objects with `getPage()`, and delegate to the page‑object methods. | + +## Flow Pattern (Pages ➜ Steps) +1. **Test begins** – a Cucumber `Given` step from `common.steps.ts` opens a URL using `getPage().goto(url)`. The shared `Page` instance is created beforehand by calling the exported `initializeBrowser` and `initializePage` helpers (usually in a test‑setup hook). +2. **Step → Page Object** – Each subsequent step imports the relevant page‑object class and calls a method on a fresh instance, e.g.: + ```ts + await new Login(getPage()).loginAsUser('standard_user'); + ``` + The page‑object contains **locators** defined as class fields and performs actions with `this.page.locator()`. +3. **Assertions** – Validation steps call methods like `validateTitle` or `validateErrorMessage` that internally retrieve a locator, check visibility/text, and throw descriptive errors if the expectation is not met. +4. **Teardown** – After the scenario finishes, the framework (or a global `AfterAll` hook) calls `closeBrowser()` to shut down the Playwright instance. + +## Assessment Locator +The only *assessment*‑type locator currently present is the **error message locator** used to verify login failures: +```ts +const errorLocator = this.page.locator('[data-test="error"]'); +``` +- It is defined inside `Login.validateErrorMessage` (see `pages/login.page.ts`). +- The step `Then('I should see error message {string}', …)` triggers this validation. +- When adding new assessments, follow the same pattern: + 1. Define a **private readonly** selector string (or inline locator) in the relevant page class. + 2. Expose a public validation method that checks visibility and text content. + 3. Add a corresponding Cucumber step that calls the method. + +## Adding New Pages or Steps +1. **Create a page object** in `pages/`: + ```ts + export class NewPage { + private readonly page: Page; + // example locator + private readonly submitBtn = 'button[id="submit"]'; + + constructor(page: Page) { this.page = page; } + + async clickSubmit() { await this.page.locator(this.submitBtn).click(); } + } + ``` +2. **Create a step** in `steps/` that uses the page object: + ```ts + Then('I submit the form', async () => { + await new NewPage(getPage()).clickSubmit(); + }); + ``` +3. **Re‑use existing utilities** – never instantiate a new `Browser` or `Page` inside steps; always rely on `getPage()`. + +## Summary for Cline +- **Pattern**: *Given* → open URL → *Then* steps → page‑object methods → assertions. +- **Locators** are stored as class fields in the page objects; validation (assessment) locators follow the same convention. +- **Tools**: only `playwrightUtilities.ts` functions are needed for browser lifecycle management. +- When extending the suite, keep the folder conventions, reuse `getPage()`, and add new locators/validation methods in the relevant page class. + +--- +*This guide is intended for the Cline AI agent to understand the project's navigation, locator conventions, and available helper utilities.* \ No newline at end of file diff --git a/cucumber.js b/cucumber.js index e8192d63..61ce55ae 100644 --- a/cucumber.js +++ b/cucumber.js @@ -1,3 +1,3 @@ module.exports = { - default: `--require-module ts-node/register --require './steps/**/*.ts' --require './hooks/**/*.ts --format @cucumber/pretty-formatter` + default: `--require-module ts-node/register --require './steps/**/*.ts' --require './hooks/**/*.ts' --format @cucumber/pretty-formatter` }; \ No newline at end of file diff --git a/features/login.feature b/features/login.feature index fb9f1fa5..47aa66ad 100644 --- a/features/login.feature +++ b/features/login.feature @@ -5,8 +5,10 @@ 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' + Then I should see error message "Epic sadface: Sorry, this user has been locked out." + # TODO: Add a step to validate the error message received \ No newline at end of file diff --git a/features/product.feature b/features/product.feature index 8a7ceab9..c4599458 100644 --- a/features/product.feature +++ b/features/product.feature @@ -3,11 +3,15 @@ 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 - Then I will login as 'standard_user' - # TODO: Sort the items by - # TODO: Validate all 6 items are sorted correctly by price + Scenario Outline: Validate product sort by price + Then I will login as 'standard_user' + # TODO: Add a step to login as 'standard_user' + Then I sort items by "" + # TODO: Add a step to sort items by "" + Then I validate items are sorted by price "" + # TODO: Add a step to validate sorted items 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) | diff --git a/features/purchase.feature b/features/purchase.feature index 28634789..2f65e19f 100644 --- a/features/purchase.feature +++ b/features/purchase.feature @@ -6,7 +6,13 @@ Feature: Purchase Feature 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 select the cart + Then I select Checkout + Then I fill in the First Name "John", Last Name "Doe", and Zip/Postal Code "12345" + Then I select Continue + Then I select Finish + Then I validate the text "Thank you for your order!" + # TODO: Select Checkout # TODO: Fill in the First Name, Last Name, and Zip/Postal Code # TODO: Select Continue diff --git a/package-lock.json b/package-lock.json index b90d7c6c..b26590ba 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": { @@ -189,6 +189,7 @@ "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-10.0.1.tgz", "integrity": "sha512-g7W7SQnNMSNnMRQVGubjefCxdgNFyq4P3qxT2Ve7Xhh8ZLoNkoRDcWsyfKQVWnxNfgW3aGJmxbucWRoTi+ZUqg==", "dev": true, + "peer": true, "dependencies": { "@cucumber/ci-environment": "9.2.0", "@cucumber/cucumber-expressions": "16.1.2", @@ -253,6 +254,7 @@ "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-26.2.0.tgz", "integrity": "sha512-iRSiK8YAIHAmLrn/mUfpAx7OXZ7LyNlh1zT89RoziSVCbqSVDxJS6ckEzW8loxs+EEXl0dKPQOXiDmbHV+C/fA==", "dev": true, + "peer": true, "dependencies": { "@cucumber/messages": ">=19.1.4 <=22" } @@ -350,6 +352,7 @@ "resolved": "https://registry.npmjs.org/@cucumber/message-streams/-/message-streams-4.0.1.tgz", "integrity": "sha512-Kxap9uP5jD8tHUZVjTWgzxemi/0uOsbGjd4LBOSxcJoOCRbESFwemUzilJuzNTB8pcTQUh8D5oudUyxfkJOKmA==", "dev": true, + "peer": true, "peerDependencies": { "@cucumber/messages": ">=17.1.1" } @@ -359,6 +362,7 @@ "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-22.0.0.tgz", "integrity": "sha512-EuaUtYte9ilkxcKmfqGF9pJsHRUU0jwie5ukuZ/1NPTuHS1LxHPsGEODK17RPRbZHOFhqybNzG2rHAwThxEymg==", "dev": true, + "peer": true, "dependencies": { "@types/uuid": "9.0.1", "class-transformer": "0.5.1", @@ -549,6 +553,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz", "integrity": "sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==", "dev": true, + "peer": true, "dependencies": { "undici-types": "~5.26.4" } @@ -974,6 +979,7 @@ "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-21.0.1.tgz", "integrity": "sha512-pGR7iURM4SF9Qp1IIpNiVQ77J9kfxMkPOEbyy+zRmGABnWWCsqMpJdfHeh9Mb3VskemVw85++e15JT0PYdcR3g==", "dev": true, + "peer": true, "dependencies": { "@types/uuid": "8.3.4", "class-transformer": "0.5.1", @@ -2473,6 +2479,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/pages/login.page.ts b/pages/login.page.ts index 5a01614b..6e458de8 100644 --- a/pages/login.page.ts +++ b/pages/login.page.ts @@ -19,8 +19,27 @@ export class Login { } public async loginAsUser(userName: string) { - await this.page.locator(this.userNameField).fill(userName) - await this.page.locator(this.passwordField).fill(this.password) - await this.page.locator(this.loginButton).click() + await this.page.locator(this.userNameField).fill(userName); + await this.page.locator(this.passwordField).fill(this.password); + await this.page.locator(this.loginButton).click({ noWaitAfter: true }); + // Wait for navigation to the inventory page after successful login, but ignore if login fails (e.g., locked out user). + + try { + await this.page.waitForURL('**/inventory.html', { timeout: 5000 }); + } catch (e) { + // Navigation didn't happen, likely due to login error; continue without throwing. + } + } + + public async validateErrorMessage(expectedMessage: string) { + const errorLocator = this.page.locator('[data-test="error"]'); + const visible = await errorLocator.isVisible(); + if (!visible) { + throw new Error('Error message is not visible'); + } + const actualMessage = await errorLocator.textContent(); + if (actualMessage?.trim() !== expectedMessage.trim()) { + throw new Error(`Expected error message to be "${expectedMessage}" but got "${actualMessage}"`); + } } } \ No newline at end of file diff --git a/pages/product.page.ts b/pages/product.page.ts index 14bedb1b..77278b80 100644 --- a/pages/product.page.ts +++ b/pages/product.page.ts @@ -1,14 +1,43 @@ -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 sortSelect: string = 'select[data-test="product-sort-container"]'; + private readonly priceLabels: string = '.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(); } -} \ No newline at end of file + + /** + * Sort items using the dropdown on the inventory page. + * @param option The visible label of the option, e.g. "Price (low to high)". + */ + public async sortBy(option: string) { + // Ensure we are on the inventory page before interacting. + await this.page.waitForURL('**/inventory.html', { timeout: 15000 }); + // Wait for the inventory items to be loaded. + await this.page.waitForSelector('.inventory_item', { timeout: 15000 }); + // Ensure the sort dropdown is visible. + await this.page.waitForSelector(this.sortSelect, { state: 'visible', timeout: 15000 }); + await this.page.locator(this.sortSelect).selectOption({ label: option }); + } + + /** + * Validate that the list of prices is sorted according to the requested order. + * @param order "low" for ascending (low to high) or "high" for descending (high to low). + */ + public async validateSorted(order: 'low' | 'high') { + const priceTexts = await this.page.locator(this.priceLabels).allTextContents(); + const prices = priceTexts.map(t => parseFloat(t.replace('$', '').trim())); + const sorted = [...prices].sort((a, b) => (order === 'low' ? a - b : b - a)); + if (JSON.stringify(prices) !== JSON.stringify(sorted)) { + throw new Error(`Prices are not sorted correctly for order '${order}'. Expected ${sorted}, got ${prices}`); + } + } +} diff --git a/pages/purchase.page.ts b/pages/purchase.page.ts new file mode 100644 index 00000000..cc39b278 --- /dev/null +++ b/pages/purchase.page.ts @@ -0,0 +1,46 @@ +import { Page } from '@playwright/test'; + +export class Purchase { + private readonly page: Page; + private readonly cartLink: string = '.shopping_cart_link'; + private readonly checkoutButton: string = '#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 = '#continue'; + private readonly finishButton: string = '#finish'; + private readonly confirmationHeader: string = '.complete-header'; + + constructor(page: Page) { + this.page = page; + } + + public async selectCart() { + await this.page.locator(this.cartLink).click(); + } + + public async selectCheckout() { + await this.page.locator(this.checkoutButton).click(); + } + + public async fillInfo(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.postalCodeField).fill(zip); + } + + public async selectContinue() { + await this.page.locator(this.continueButton).click(); + } + + public async selectFinish() { + await this.page.locator(this.finishButton).click(); + } + + public async validateConfirmation(expected: string) { + const actual = await this.page.locator(this.confirmationHeader).textContent(); + if (!actual || actual.trim() !== expected.trim()) { + throw new Error(`Expected confirmation text to be "${expected}" but got "${actual}"`); + } + } +} diff --git a/steps/login.steps.ts b/steps/login.steps.ts index c2aa0d80..00fe593c 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 error message {string}', async (expectedMessage) => { + await new Login(getPage()).validateErrorMessage(expectedMessage); }); \ No newline at end of file diff --git a/steps/product.steps.ts b/steps/product.steps.ts index bb52fb98..efb2ab4f 100644 --- a/steps/product.steps.ts +++ b/steps/product.steps.ts @@ -4,4 +4,13 @@ import { Product } from '../pages/product.page'; Then('I will add the backpack to the cart', async () => { await new Product(getPage()).addBackPackToCart(); +}); + +Then('I sort items by {string}', { timeout: 60000 }, async (option) => { + await new Product(getPage()).sortBy(option); +}); + +Then('I validate items are sorted by price {string}', async (order) => { + const normalized = order.toLowerCase().includes('high to low') ? 'high' : 'low'; + await new Product(getPage()).validateSorted(normalized as 'low' | 'high'); }); \ No newline at end of file diff --git a/steps/purchase.steps.ts b/steps/purchase.steps.ts new file mode 100644 index 00000000..3a4d842d --- /dev/null +++ b/steps/purchase.steps.ts @@ -0,0 +1,27 @@ +import { Then } from '@cucumber/cucumber'; +import { getPage } from '../playwrightUtilities'; +import { Purchase } from '../pages/purchase.page'; + +Then('I select the cart', async () => { + await new Purchase(getPage()).selectCart(); +}); + +Then('I select Checkout', async () => { + await new Purchase(getPage()).selectCheckout(); +}); + +Then(/^I fill in the First Name "(.*)", Last Name "(.*)", and Zip\/Postal Code "(.*)"$/, async (firstName, lastName, zip) => { + await new Purchase(getPage()).fillInfo(firstName, lastName, zip); +}); + +Then('I select Continue', async () => { + await new Purchase(getPage()).selectContinue(); +}); + +Then('I select Finish', async () => { + await new Purchase(getPage()).selectFinish(); +}); + +Then('I validate the text {string}', async (expected) => { + await new Purchase(getPage()).validateConfirmation(expected); +}); From 0331e427d5ca6e61b778cbeae2da8d1f7965eef1 Mon Sep 17 00:00:00 2001 From: bako678 <119353136+bako678@users.noreply.github.com> Date: Fri, 7 Aug 2026 14:30:37 -0500 Subject: [PATCH 2/2] Revise README for test updates and new features Updated the README to reflect changes in test scenarios and added new features related to product sorting and validation. --- README.md | 72 ++++++++++++++++++------------------------------------- 1 file changed, 23 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index b9111057..4beeacdf 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,32 @@ -# Sample Playwright Automation Test +# Changelog -## System Requirements +## 2026-08-07 (2) -node >= v18.5.x +### Fixed +- **`pages/product.page.ts`**: Updated the sort-dropdown selector from `select[data-test="product_sort_container"]` to `select[data-test="product-sort-container"]` to match saucedemo.com's current markup (attribute was renamed with a hyphen), which was causing `sortBy` to time out waiting for the dropdown. +- **`steps/product.steps.ts`**: Fixed the price-order normalization in the `I validate items are sorted by price {string}` step — `"Price (high to low)"` was incorrectly matching the `.includes('low')` check and being treated as ascending order, silently mis-validating the descending-sort scenario. +- **`cucumber.js`**: Fixed a missing closing quote after `./hooks/**/*.ts` in the `default` profile string, which caused the `--format` flag to be swallowed into the require glob and broke `npm test` with a "Cucumber instance isn't running" error. -npm >= v7 +All Cucumber/Playwright tests now pass (5 scenarios, 22 steps) via `npm test`. +## 2026-08-07 -## Setup +### Added +- **Product Feature**: Implemented sorting by price with new step definitions `I sort items by {string}` and `I validate items are sorted by price {string}`. +- Added `sortBy` and `validateSorted` methods to `pages/product.page.ts` to interact with the sort dropdown and verify price order. +- Updated `features/product.feature` to use a Scenario Outline with examples for low‑to‑high and high‑to‑low sorting. +- Updated `steps/product.steps.ts` with corresponding step implementations. -// Install Visual Studio Code (or any editor) +### Existing additions (previous) +- **Step Definition**: `I should see error message {string}` in `steps/login.steps.ts` to validate login error messages. +- **Page Method**: `validateErrorMessage(expectedMessage: string)` in `pages/login.page.ts` for checking error visibility and content. +- **Feature Update**: Updated `features/login.feature` scenario *Validate login error message* with the new step to assert the specific error text. -https://code.visualstudio.com/download +All Cucumber/Playwright tests now pass (3 scenarios, 8 steps). +### Added +- **Step Definition**: `I should see error message {string}` in `steps/login.steps.ts` to validate login error messages. +- **Page Method**: `validateErrorMessage(expectedMessage: string)` in `pages/login.page.ts` for checking error visibility and content. +- **Feature Update**: Updated `features/login.feature` scenario *Validate login error message* with the new step to assert the specific error text. -// Install Node.js - -https://nodejs.org/en/download - - -```bash -git clone https://github.com/automationExamples/Playwright-Cucumber-Exercise.git -npm install -npx playwright install -``` - -### Recommended vscode extensions - -Cucumber v1.7.0 - -Cucumber (Gherkin) Support enhanced for Behat - - -## Instructions -To run the test -```bash -npm run test -``` - -After running, to generate the cucumber report (cucumber_report.html) -```bash -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 - -### 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 +All Cucumber/Playwright tests now pass (3 scenarios, 8 steps).