diff --git a/README.md b/README.md index b911105..bb51b9e 100644 --- a/README.md +++ b/README.md @@ -47,12 +47,12 @@ It is not expected that you complete every task, however, please give your best You will be scored based on your ability to complete the following tasks: -- [ ] Install and setup this repository on your personal computer +- [X] 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 +- [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 - [ ] 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 diff --git a/features/login.feature b/features/login.feature index fb9f1fa..c4a900a 100644 --- a/features/login.feature +++ b/features/login.feature @@ -5,8 +5,9 @@ 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' - # TODO: Add a step to validate the error message received \ No newline at end of file + # TODO: Add a step to validate the error message received + Then I should see an 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..4678b4b 100644 --- a/features/product.feature +++ b/features/product.feature @@ -7,7 +7,11 @@ Feature: Product Feature Scenario Outline: Validate product sort by price Then I will login as 'standard_user' # TODO: Sort the items by + Then I will sort items by '' # TODO: Validate all 6 items are sorted correctly by price + Then the items should be sorted by price '' Examples: # TODO: extend the datatable to paramterize this test - | sort | \ No newline at end of file + | sort | + | high to low | + | low to high | \ No newline at end of file diff --git a/features/purchase.feature b/features/purchase.feature index 2863478..3fd3e94 100644 --- a/features/purchase.feature +++ b/features/purchase.feature @@ -7,8 +7,14 @@ Feature: Purchase Feature Then I will login as 'standard_user' Then I will add the backpack to the cart # TODO: Select the cart (top-right) + Then I will select the link to the cart # TODO: Select Checkout + Then I will click checkout # TODO: Fill in the First Name, Last Name, and Zip/Postal Code + Then I will complete the checkout form # TODO: Select Continue + Then I will click continue # TODO: Select Finish - # TODO: Validate the text 'Thank you for your order!' \ No newline at end of file + Then I will click finish + # TODO: Validate the text 'Thank you for your order!' + Then I should see the order complete message 'Thank you for your order!' \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b90d7c6..c58324d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,16 @@ { - "name": "Playwright-Project", + "name": "Playwright-Cucumber-Exercise", "lockfileVersion": 3, "requires": true, "packages": { "": { + "dependencies": { + "playwright": "^1.58.2" + }, "devDependencies": { "@cucumber/cucumber": "^10.0.1", "@cucumber/pretty-formatter": "^1.0.0", - "@playwright/test": "^1.40.1", + "@playwright/test": "^1.58.2", "@types/node": "^20.10.3", "cucumber-html-reporter": "^7.1.1", "ts-node": "^10.9.1", @@ -491,18 +494,19 @@ } }, "node_modules/@playwright/test": { - "version": "1.40.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.1.tgz", - "integrity": "sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==", + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", + "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "playwright": "1.40.1" + "playwright": "1.58.2" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@teppeis/multimaps": { @@ -1306,8 +1310,8 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1900,33 +1904,33 @@ } }, "node_modules/playwright": { - "version": "1.40.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.40.1.tgz", - "integrity": "sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw==", - "dev": true, + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", + "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.40.1" + "playwright-core": "1.58.2" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" }, "optionalDependencies": { "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.40.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz", - "integrity": "sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==", - "dev": true, + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", + "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/progress": { diff --git a/package.json b/package.json index ed38f6f..551acd1 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,13 @@ "devDependencies": { "@cucumber/cucumber": "^10.0.1", "@cucumber/pretty-formatter": "^1.0.0", - "@playwright/test": "^1.40.1", + "@playwright/test": "^1.58.2", "@types/node": "^20.10.3", "cucumber-html-reporter": "^7.1.1", "ts-node": "^10.9.1", "typescript": "^5.3.2" + }, + "dependencies": { + "playwright": "^1.58.2" } } diff --git a/pages/login.page.ts b/pages/login.page.ts index 5a01614..9e52cbf 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 @@ -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; @@ -23,4 +24,10 @@ export class Login { await this.page.locator(this.passwordField).fill(this.password) await this.page.locator(this.loginButton).click() } + + public async loginErrorMessage(expectedMessage:string){ + const errorMessage = await this.page.locator(this.errorMessage).innerText() + expect(errorMessage).toBe(expectedMessage) + + } } \ No newline at end of file diff --git a/pages/product.page.ts b/pages/product.page.ts index 14bedb1..1c83e79 100644 --- a/pages/product.page.ts +++ b/pages/product.page.ts @@ -1,8 +1,12 @@ -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 cartLink: string = '[data-test="shopping-cart-link"]' + private readonly sortDropdownContainer: string = '[data-test="product-sort-container"]' + private readonly itemPrice: string = '[data-test="inventory-item-price"]' + constructor(page: Page) { this.page = page; @@ -11,4 +15,22 @@ export class Product { public async addBackPackToCart() { await this.page.locator(this.addToCart).click() } + + public async selectCart(){ + await this.page.locator(this.cartLink).click() + } + + public async sortByPrice(sort: string) { + const value = sort === 'low to high' ? 'lohi' : 'hilo' + await this.page.locator(this.sortDropdownContainer).selectOption(value) + } + + public async validatePriceSorting(sort: string) { + const itemsPrices = await this.page.locator(this.itemPrice).allInnerTexts() + const prices = itemsPrices.map(p => parseFloat(p.replace('$', ''))) + const sorted = [...prices].sort((a, b) => sort === 'low to high' ? a - b : b - a) + expect(prices).toEqual(sorted) + } + + } \ No newline at end of file diff --git a/pages/purchase.page.ts b/pages/purchase.page.ts new file mode 100644 index 0000000..90ae202 --- /dev/null +++ b/pages/purchase.page.ts @@ -0,0 +1,45 @@ +import { expect, Page } from "@playwright/test" + +export class Purchase { + private readonly page: Page + private readonly firstName: string = 'Testy' + private readonly lastName: string = 'McTester' + private readonly zipcode: string = '12345' + private readonly firstNameField: string = 'input[id="first-name"]' + private readonly lastNameField: string = 'input[id="last-name"]' + private readonly zipcodeField: string = 'input[id="postal-code"]' + private readonly cartCheckoutButton: string = 'button[id="checkout"]' + private readonly checkoutContinueButton: string = 'input[id="continue"]' + private readonly finishCheckoutButton: string = 'button[id="finish"]' + private readonly orderCompleteText: string = '[data-test="complete-header"]' + + + + constructor(page: Page) { + this.page = page; + } + + public async selectCheckout(){ + await this.page.locator(this.cartCheckoutButton).click() + } + + public async completeCheckoutForm() { + await this.page.locator(this.firstNameField).fill(this.firstName) + await this.page.locator(this.lastNameField).fill(this.lastName) + await this.page.locator(this.zipcodeField).fill(this.zipcode) + } + + public async continueCheckout() { + await this.page.locator(this.checkoutContinueButton).click() + } + + public async finishCheckout() { + await this.page.locator(this.finishCheckoutButton).click() + } + + public async seeOrderCompleteStatus(expectedMessage: string){ + const orderCompleteMessage = await this.page.locator(this.orderCompleteText).innerText() + expect(orderCompleteMessage).toBe(expectedMessage) + + } +} \ No newline at end of file diff --git a/steps/login.steps.ts b/steps/login.steps.ts index c2aa0d8..c251c74 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 an the error message {string}', async (expectedMessage) => { + await new Login(getPage()).loginErrorMessage(expectedMessage); }); \ No newline at end of file diff --git a/steps/product.steps.ts b/steps/product.steps.ts index bb52fb9..ec57259 100644 --- a/steps/product.steps.ts +++ b/steps/product.steps.ts @@ -4,4 +4,16 @@ import { Product } from '../pages/product.page'; Then('I will add the backpack to the cart', async () => { await new Product(getPage()).addBackPackToCart(); +}); + +Then('I will select the link to the cart', async () => { + await new Product(getPage()).selectCart(); +}); + +Then('I will sort items by {string}', async (sort: string) => { + await new Product(getPage()).sortByPrice(sort); +}); + +Then('the items should be sorted by price {string}', async (sort: string) => { + await new Product(getPage()).validatePriceSorting(sort); }); \ No newline at end of file diff --git a/steps/purchase.steps.ts b/steps/purchase.steps.ts new file mode 100644 index 0000000..e6e83e5 --- /dev/null +++ b/steps/purchase.steps.ts @@ -0,0 +1,26 @@ +import { Then } from '@cucumber/cucumber'; +import { getPage } from '../playwrightUtilities'; +import { Purchase } from '../pages/purchase.page'; + + +Then('I will click checkout', async () => { + await new Purchase(getPage()).selectCheckout(); +}); + + +Then('I will complete the checkout form', async () => { + await new Purchase(getPage()).completeCheckoutForm(); +}); + + +Then('I will click continue', async () => { + await new Purchase(getPage()).continueCheckout(); +}); + +Then('I will click finish', async () => { + await new Purchase(getPage()).finishCheckout(); +}); + +Then ('I should see the order complete message {string}', async (expectedMessage) => { + await new Purchase(getPage()).seeOrderCompleteStatus(expectedMessage) +}) \ No newline at end of file