Skip to content

Commit a36acc7

Browse files
Introduced this keyword to actions method to help parallel execution
1 parent cd124eb commit a36acc7

File tree

12 files changed

+152
-283
lines changed

12 files changed

+152
-283
lines changed

.gitignore

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ package-lock.json
1010
logs/
1111

1212
# # Test outputs and artifacts (generated by cucumber/playwright)
13-
# tests/test-results/
14-
# tests/test-results/**/*
15-
# tests/reports/
16-
# tests/playwright-report/
17-
# tests/logs/
18-
# tests/.cache/
19-
# tests/reports/**/*.*
20-
# tests/test-results/videos/
21-
# tests/test-results/videos/**/*.*
22-
# tests/test-results/screenshots/
23-
# tests/test-results/screenshots/**/*
13+
tests/test-results/
14+
tests/test-results/**/*
15+
tests/reports/
16+
tests/playwright-report/
17+
tests/logs/
18+
tests/.cache/
19+
tests/reports/**/*.*
20+
tests/test-results/videos/
21+
tests/test-results/videos/**/*.*
22+
tests/test-results/screenshots/
23+
tests/test-results/screenshots/**/*

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
"types": "dist/index.d.ts",
77
"scripts": {
88
"build": "tsc",
9-
"test:bdd:ui": "cross-env node tests/run-cucumber.js",
10-
"test:ui:parallel": "cross-env node tests/run-cucumber.parallel.js",
9+
"test:ui": "cross-env node tests/run-cucumber.js",
10+
"test:ui:parallel": "cross-env PARALLEL=2 node tests/run-cucumber.js",
1111
"lint": "eslint src tests",
1212
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
1313
"init": "playwright install && npm install"

tests/pages/base.page.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

tests/pages/cart.page.ts

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,72 +21,68 @@ type CartItem = {
2121
quantity: number;
2222
};
2323

24-
let actions: WebActions;
25-
let page: Page;
26-
2724
/**
2825
* Cart page actions
2926
*/
30-
export const cartPage = {
31-
setPage(newPage: Page) {
32-
page = newPage;
33-
actions = new WebActions(page);
34-
},
27+
export class CartPage {
28+
constructor(
29+
private page: Page,
30+
private actions: WebActions
31+
) {}
3532

3633
/**
3734
* Get all items currently in the cart
3835
*/
3936
async getCartItems(): Promise<CartItem[]> {
40-
const cartItems = [];
41-
const items = await page.$$eval(Locators.CART_ITEM, (elements: HTMLElement[]) => {
37+
const items = await this.page.$$eval(Locators.CART_ITEM, (elements: HTMLElement[]) => {
4238
return elements.map((el) => {
43-
const name = el.querySelector(Locators.ITEM_NAME)?.textContent || '';
44-
const price = el.querySelector(Locators.ITEM_PRICE)?.textContent || '';
39+
const name = el.querySelector('.inventory_item_name')?.textContent || '';
40+
const price = el.querySelector('.inventory_item_price')?.textContent || '';
4541
return { name, price, quantity: 1 }; // Default quantity is 1 as SauceDemo doesn't support quantity changes
4642
});
4743
});
4844
return items;
49-
},
45+
}
5046

5147
/**
5248
* Click checkout button to proceed to checkout
5349
*/
5450
async proceedToCheckout(): Promise<void> {
55-
await actions.click(Locators.CHECKOUT_BUTTON);
56-
},
51+
await this.actions.click(Locators.CHECKOUT_BUTTON);
52+
}
5753

5854
/**
5955
* Fill checkout information form
6056
*/
6157
async fillCheckoutInfo(firstName: string, lastName: string, postalCode: string): Promise<void> {
62-
await actions.fill(Locators.FIRST_NAME_INPUT, firstName);
63-
await actions.fill(Locators.LAST_NAME_INPUT, lastName);
64-
await actions.fill(Locators.POSTAL_CODE_INPUT, postalCode);
65-
await actions.click(Locators.CONTINUE_BUTTON);
66-
},
58+
await this.actions.fill(Locators.FIRST_NAME_INPUT, firstName);
59+
await this.actions.fill(Locators.LAST_NAME_INPUT, lastName);
60+
await this.actions.fill(Locators.POSTAL_CODE_INPUT, postalCode);
61+
await this.actions.click(Locators.CONTINUE_BUTTON);
62+
}
6763

6864
/**
6965
* Complete the order by clicking finish button
7066
*/
7167
async completeOrder(): Promise<void> {
72-
await actions.click(Locators.FINISH_BUTTON);
73-
},
68+
await this.actions.click(Locators.FINISH_BUTTON);
69+
}
7470

7571
/**
7672
* Get the confirmation message after order completion
7773
*/
7874
async getConfirmationMessage(): Promise<string | null> {
79-
const messageElement = await page.$(Locators.CONFIRMATION_HEADER);
75+
const messageElement = await this.page.$(Locators.CONFIRMATION_HEADER);
8076
return messageElement ? await messageElement.textContent() : null;
81-
},
77+
}
8278

8379
/**
8480
* Remove a specific item from cart by product name
85-
*/
81+
*/
8682
async removeItem(productName: string): Promise<void> {
8783
const selector = `//div[text()="${productName}"]/ancestor::div[@class="cart_item"]//button[contains(@id, "remove")]`;
88-
await actions.click(selector);
89-
},
84+
await this.actions.click(selector);
85+
}
9086

9187
/**
9288
* Calculate total price of items in cart

tests/pages/login.page.ts

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,58 +12,52 @@ const Locators = {
1212

1313
const URL = 'https://www.saucedemo.com/';
1414

15-
let actions: WebActions;
16-
let page: Page;
17-
1815
/**
1916
* Login page actions
2017
*/
21-
export const loginPage = {
22-
/**
23-
* Initialize the page
24-
*/
25-
setPage(newPage: Page) {
26-
page = newPage;
27-
actions = new WebActions(page);
28-
},
18+
export class LoginPage {
19+
constructor(
20+
private page: Page,
21+
private actions: WebActions
22+
) {}
2923

3024
/**
3125
* Navigate to the login page
3226
*/
3327
async navigateToLogin(): Promise<void> {
34-
await actions.navigateTo(URL);
35-
},
28+
await this.actions.navigateTo(URL);
29+
}
3630

3731
/**
3832
* Login with the given credentials
3933
* @param username Username (defaults to standard_user)
4034
* @param password Password (defaults to secret_sauce)
4135
*/
4236
async login(username: string = 'standard_user', password: string = 'secret_sauce'): Promise<void> {
43-
await actions.fill(Locators.USERNAME_INPUT, username);
44-
await actions.fill(Locators.PASSWORD_INPUT, password);
45-
await actions.click(Locators.LOGIN_BUTTON);
46-
},
37+
await this.actions.fill(Locators.USERNAME_INPUT, username);
38+
await this.actions.fill(Locators.PASSWORD_INPUT, password);
39+
await this.actions.click(Locators.LOGIN_BUTTON);
40+
}
4741

4842
/**
4943
* Get the error message if login fails
5044
* @returns The error message text or null if no error
5145
*/
5246
async getErrorMessage(): Promise<string | null> {
53-
const errorElement = await page.$(Locators.ERROR_MESSAGE);
47+
const errorElement = await this.page.$(Locators.ERROR_MESSAGE);
5448
return errorElement ? await errorElement.textContent() : null;
55-
},
49+
}
5650

5751
/**
5852
* Check if user is logged in by looking for the inventory list
5953
* @returns true if logged in, false otherwise
6054
*/
6155
async isLoggedIn(): Promise<boolean> {
6256
try {
63-
await page.waitForSelector(Locators.INVENTORY_LIST, { timeout: 5000 });
57+
await this.page.waitForSelector(Locators.INVENTORY_LIST, { timeout: 5000 });
6458
return true;
6559
} catch {
6660
return false;
6761
}
6862
}
69-
} as const;
63+
}

tests/pages/products.page.ts

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,55 +11,49 @@ const Locators = {
1111
ITEM_NAME: '.inventory_item_name'
1212
} as const;
1313

14-
let actions: WebActions;
15-
let page: Page;
16-
1714
/**
1815
* Products page actions
1916
*/
20-
export const productsPage = {
21-
/**
22-
* Initialize the page
23-
*/
24-
setPage(newPage: Page) {
25-
page = newPage;
26-
actions = new WebActions(page);
27-
},
17+
export class ProductsPage {
18+
constructor(
19+
private page: Page,
20+
private actions: WebActions
21+
) {}
2822

2923
/**
3024
* Add a product to cart by its name
3125
* @param productName The name of the product to add
3226
*/
3327
async addToCart(productName: string): Promise<void> {
3428
const selector = `//div[text()="${productName}"]/ancestor::div[@class="inventory_item"]//button[contains(@id, "add-to-cart")]`;
35-
await actions.click(selector);
36-
},
29+
await this.actions.click(selector);
30+
}
3731

3832
/**
3933
* Remove a product from cart by its name
4034
* @param productName The name of the product to remove
4135
*/
4236
async removeFromCart(productName: string): Promise<void> {
4337
const selector = `//div[text()="${productName}"]/ancestor::div[@class="inventory_item"]//button[contains(@id, "remove")]`;
44-
await actions.click(selector);
45-
},
38+
await this.actions.click(selector);
39+
}
4640

4741
/**
4842
* Get the number of items in the cart
4943
* @returns The number of items in cart, 0 if cart is empty
5044
*/
5145
async getCartItemsCount(): Promise<number> {
52-
const badge = await page.$(Locators.CART_BADGE);
46+
const badge = await this.page.$(Locators.CART_BADGE);
5347
const text = badge ? await badge.textContent() : null;
5448
return text ? parseInt(text) : 0;
55-
},
49+
}
5650

5751
/**
5852
* Navigate to the cart page
5953
*/
6054
async goToCart(): Promise<void> {
61-
await actions.click(Locators.CART_LINK);
62-
},
55+
await this.actions.click(Locators.CART_LINK);
56+
}
6357

6458
/**
6559
* Get the price of a specific product
@@ -68,24 +62,24 @@ export const productsPage = {
6862
*/
6963
async getProductPrice(productName: string): Promise<string | null> {
7064
const selector = `//div[text()="${productName}"]/ancestor::div[@class="inventory_item"]//div[@class="inventory_item_price"]`;
71-
const element = await page.$(selector);
65+
const element = await this.page.$(selector);
7266
return element ? element.textContent() : null;
73-
},
67+
}
7468

7569
/**
7670
* Sort products by the given option
7771
* @param option Sort option: az (A to Z), za (Z to A), lohi (Low to High), hilo (High to Low)
7872
*/
7973
async sortProducts(option: 'az' | 'za' | 'lohi' | 'hilo'): Promise<void> {
80-
await page.selectOption(Locators.SORT_DROPDOWN, option);
81-
},
74+
await this.page.selectOption(Locators.SORT_DROPDOWN, option);
75+
}
8276

8377
/**
8478
* Get all products from the inventory
8579
* @returns Array of products with their names and prices
8680
*/
8781
async getAllProducts(): Promise<{ name: string; price: string; }[]> {
88-
const products = await page.$$(Locators.INVENTORY_ITEM);
82+
const products = await this.page.$$(Locators.INVENTORY_ITEM);
8983
const result = [];
9084

9185
for (const product of products) {

0 commit comments

Comments
 (0)