diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..b58b603f
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/Playwright-Cucumber-Exercise.iml b/.idea/Playwright-Cucumber-Exercise.iml
new file mode 100644
index 00000000..d0876a78
--- /dev/null
+++ b/.idea/Playwright-Cucumber-Exercise.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 00000000..105ce2da
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 00000000..91d4f8f5
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..35eb1ddf
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/features/login.feature b/features/login.feature
index fb9f1fa5..66925fdb 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'
- # 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 the url should be "https://www.saucedemo.com/"
+ Then I should see the error "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 8a7ceab9..afb25037 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 the items from ""
# TODO: Validate all 6 items are sorted correctly by price
+ Then I should see the products listed by price from ""
Examples:
- # TODO: extend the datatable to paramterize this test
- | sort |
\ No newline at end of file
+ # TODO: extend the datatable to parameterize this test
+ | sort |
+ | low to high |
+ | high to low |
\ No newline at end of file
diff --git a/features/purchase.feature b/features/purchase.feature
index 28634789..f1f3ae28 100644
--- a/features/purchase.feature
+++ b/features/purchase.feature
@@ -4,11 +4,17 @@ 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 with 'standard_user' as the user
Then I will add the backpack to the cart
# TODO: Select the cart (top-right)
+ Then I will go 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 fill in my First Name, Last Name, and Postal Code
# TODO: Select Continue
+ Then I will select 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 header 'Thank you for your order!'
\ No newline at end of file
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..90d1daa1 100644
--- a/pages/login.page.ts
+++ b/pages/login.page.ts
@@ -1,4 +1,5 @@
import { Page } from "@playwright/test"
+import exp from "node:constants";
export class Login {
private readonly page: Page
@@ -6,6 +7,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 errorBox:string = 'h3[data-test="error"]'
constructor(page: Page) {
this.page = page;
@@ -23,4 +25,27 @@ export class Login {
await this.page.locator(this.passwordField).fill(this.password)
await this.page.locator(this.loginButton).click()
}
-}
\ No newline at end of file
+
+ public async validateUrl(expectedUrl:string)
+ {
+ const pageUrl = this.page.url();
+ if (pageUrl !== expectedUrl)
+ {
+ throw new Error(`Expected url to be ${expectedUrl} but found ${pageUrl}`)
+ }
+ }
+
+ public async validateError(expectedError: string)
+ {
+ const errorText:string|null = await this.page.locator(this.errorBox).textContent()
+
+ // const errorText:string|null = await this.page.locator("").getAttribute("dir")
+
+ if (errorText !== expectedError)
+ {
+ throw new Error(`Expected error to be "${expectedError}" but found "${errorText}"`)
+ }
+ }
+}
+
+
diff --git a/pages/product.page.ts b/pages/product.page.ts
index 14bedb1b..89905e6b 100644
--- a/pages/product.page.ts
+++ b/pages/product.page.ts
@@ -3,6 +3,11 @@ 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 sortDropDown: string = 'select [class="product_sort_container"]'
+ private readonly hilo: string = 'option[value="hilo"]'
+ private readonly lohi: string = 'option[value="lohi"]'
+ private readonly inventoryList: string = 'div[class="inventory_list"]'
+
constructor(page: Page) {
this.page = page;
@@ -11,4 +16,31 @@ export class Product {
public async addBackPackToCart() {
await this.page.locator(this.addToCart).click()
}
+
+ public async sortHighToLow(){
+ await this.page.locator(this.sortDropDown).click()
+ await this.page.locator(this.hilo).click()
+ }
+
+ public async sortLowToHigh(){
+ await this.page.locator(this.sortDropDown).click()
+ await this.page.locator(this.lohi).click()
+ }
+
+ public async getListedPrices():Promise
+ {
+ let itemList = await this.page.locator(this.inventoryList).allTextContents()
+
+ throw new Error(`recieved ${itemList}`)
+
+
+
+ }
+
+
+
+
+
+
+
}
\ No newline at end of file
diff --git a/pages/purchase.page.ts b/pages/purchase.page.ts
new file mode 100644
index 00000000..4aeb2871
--- /dev/null
+++ b/pages/purchase.page.ts
@@ -0,0 +1,67 @@
+import { Page } from "@playwright/test"
+import exp from "node:constants";
+
+export class Purchase {
+ // constants
+ private readonly page: Page
+ private readonly password: string = 'secret_sauce'
+ private readonly firstName: string = 'standard'
+ private readonly lastName: string = 'user'
+ private readonly postalCode: string = '12345'
+ 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 firstNameField: string = 'input[id="first-name"]'
+ private readonly lastNameField: string = 'input[id="last-name"]'
+ private readonly postalCodeField: string = 'input[id="postal-code"]'
+ private readonly completeHeader: string = 'h2[class="complete-header"]'
+ private readonly continueButton: string = 'input[id="continue"]'
+
+
+ constructor(page: Page) {
+ this.page = page;
+ }
+
+ 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()
+ }
+
+ public async goToCart(){
+ await this.page.locator('a[class="shopping_cart_link"]').click()
+ }
+
+ public async click(clickId: string){
+ await this.page.locator(`button[id="${clickId}"]`).click()
+ }
+
+ public async clickContinue(){
+ await this.page.locator(this.continueButton).click()
+ }
+
+ public async fillInInfo(){
+ await this.page.locator(this.firstNameField).fill(this.firstName)
+ await this.page.locator(this.lastNameField).fill(this.lastName)
+ await this.page.locator(this.postalCodeField).fill(this.postalCode)
+ }
+
+ public async validateComplete(expectedHeader: string){
+ const headerText:string|null = await this.page.locator(this.completeHeader).textContent()
+
+ if (headerText !== expectedHeader)
+ {
+ throw new Error(`Expected complete header to be "${expectedHeader}" but found "${headerText}"`)
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/steps/login.steps.ts b/steps/login.steps.ts
index c2aa0d80..afbf1673 100644
--- a/steps/login.steps.ts
+++ b/steps/login.steps.ts
@@ -8,4 +8,19 @@ Then('I should see the title {string}', async (expectedTitle) => {
Then('I will login as {string}', async (userName) => {
await new Login(getPage()).loginAsUser(userName);
-});
\ No newline at end of file
+});
+
+Then('the url should be {string}', async (url) =>
+{
+ await new Login(getPage()).validateUrl(url);
+});
+
+Then('I should see the error {string}', async (errorMessage) =>
+{
+ // const login = new Login(getPage())
+ // await login.loginAsUser("locked_out_user");
+ // await login.validateError(errorMessage);
+
+ await new Login(getPage()).validateError(errorMessage)
+});
+
diff --git a/steps/product.steps.ts b/steps/product.steps.ts
index bb52fb98..143815d3 100644
--- a/steps/product.steps.ts
+++ b/steps/product.steps.ts
@@ -4,4 +4,23 @@ 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('Then I will sort the items from high to low', async () => {
+ await new Product(getPage()).sortHighToLow();
+})
+
+Then('Then I will sort the items from low to high', async () => {
+ await new Product(getPage()).sortLowToHigh();
+})
+
+Then('I should see the products listed by price from {string}', async (sort: string) =>
+{
+ await new Product(getPage()).getListedPrices()
+})
+
+Then('Then I will sort the items from{string}', async (sort:string) =>
+ {
+ await new Product(getPage()).sortHighToLow();
+ }
+)
\ No newline at end of file
diff --git a/steps/purchase.steps.ts b/steps/purchase.steps.ts
new file mode 100644
index 00000000..1b8d92e1
--- /dev/null
+++ b/steps/purchase.steps.ts
@@ -0,0 +1,42 @@
+import { Then } from '@cucumber/cucumber';
+import { getPage } from '../playwrightUtilities';
+import { Purchase } from '../pages/purchase.page';
+import { Login } from '../pages/login.page';
+import * as punycode from "node:punycode";
+
+
+Then('I will login with {string} as the user', async (userName) => {
+ await new Login(getPage()).loginAsUser(userName);
+});
+
+// Then('I will add the backpack to the cart', async () =>
+// {
+// await new Purchase(getPage()).click('add-to-cart-sauce-labs-backpack');
+// });
+
+Then('I will go to the cart', async () =>
+{
+ await new Purchase(getPage()).goToCart();
+});
+
+Then('I will click {string}', async (clickId) =>
+{
+ await new Purchase(getPage()).click(clickId);
+});
+
+Then('I will fill in my First Name, Last Name, and Postal Code', async () =>
+{
+ await new Purchase(getPage()).fillInInfo();
+});
+
+Then('I will select continue', async () =>
+{
+ await new Purchase(getPage()).clickContinue()
+});
+
+
+
+Then('I should see the header {string}', async (expectedHeader) =>
+{
+ await new Purchase(getPage()).validateComplete(expectedHeader)
+});
\ No newline at end of file