pw-element-interactions 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -110,14 +110,118 @@ test('Add random product and verify image gallery', async ({ page }) => {
110
110
 
111
111
  ---
112
112
 
113
+ ## 🔧 Fixtures: Zero-Setup Tests (Recommended)
114
+
115
+ For larger projects, manually initializing `repo` and `steps` inside every test quickly becomes repetitive. `pw-element-interactions` ships a `baseFixture` factory that injects all core dependencies automatically via Playwright's fixture system.
116
+
117
+ ### What's included
118
+
119
+ | Fixture | Type | Description |
120
+ |---|---|---|
121
+ | `steps` | `Steps` | The full Steps API, ready to use |
122
+ | `repo` | `ElementRepository` | Direct repository access for advanced locator queries |
123
+ | `interactions` | `ElementInteractions` | Raw interactions API for custom locators |
124
+ | `contextStore` | `ContextStore` | Shared in-memory store for passing data between steps |
125
+
126
+ ### 1. Create your fixture file
127
+
128
+ Call `baseFixture` once, passing your own `test` base and the path to your locator repository:
129
+
130
+ ```ts
131
+ // tests/fixtures/base.ts
132
+ import { test as base, expect } from '@playwright/test';
133
+ import { baseFixture } from 'pw-element-interactions';
134
+
135
+ export const test = baseFixture(base, 'tests/data/page-repository.json');
136
+ export { expect };
137
+ ```
138
+
139
+ ### 2. Use fixtures in your tests
140
+
141
+ Import `test` from your fixture file. All four fixtures are available as named parameters — no setup code required:
142
+
143
+ ```ts
144
+ // tests/checkout.spec.ts
145
+ import { test, expect } from '../fixtures/base';
146
+ import { DropdownSelectType } from 'pw-element-interactions';
147
+
148
+ test('Complete checkout flow', async ({ steps }) => {
149
+ await steps.navigateTo('/');
150
+ await steps.click('HomePage', 'category-accessories');
151
+ await steps.clickRandom('AccessoriesPage', 'product-cards');
152
+ await steps.verifyUrlContains('/product/');
153
+
154
+ const selectedSize = await steps.selectDropdown('ProductDetailsPage', 'size-selector', {
155
+ type: DropdownSelectType.RANDOM,
156
+ });
157
+
158
+ await steps.verifyImages('ProductDetailsPage', 'gallery-images');
159
+ await steps.click('ProductDetailsPage', 'add-to-cart-button');
160
+ await steps.waitForState('CheckoutPage', 'confirmation-modal', 'visible');
161
+ });
162
+ ```
163
+
164
+ ### 3. Access `repo` directly when needed
165
+
166
+ For advanced queries like resolving a locator by visible text, destructure `repo` alongside `steps`:
167
+
168
+ ```ts
169
+ test('Navigate to Forms category', async ({ page, repo, steps }) => {
170
+ await steps.navigateTo('/');
171
+
172
+ const formsLink = await repo.getByText(page, 'HomePage', 'categories', 'Forms');
173
+ await formsLink?.click();
174
+
175
+ await steps.verifyAbsence('HomePage', 'categories');
176
+ });
177
+ ```
178
+
179
+ ### 4. Extend with your own fixtures
180
+
181
+ Because `baseFixture` returns a standard Playwright `test` object, you can chain your own fixtures on top of it cleanly:
182
+
183
+ ```ts
184
+ // tests/fixtures/base.ts
185
+ import { test as base } from '@playwright/test';
186
+ import { baseFixture } from 'pw-element-interactions';
187
+ import { AuthService } from '../services/AuthService';
188
+
189
+ type MyFixtures = {
190
+ authService: AuthService;
191
+ };
192
+
193
+ const testWithBase = baseFixture(base, 'tests/data/page-repository.json');
194
+
195
+ export const test = testWithBase.extend<MyFixtures>({
196
+ authService: async ({ page }, use) => {
197
+ await use(new AuthService(page));
198
+ },
199
+ });
200
+
201
+ export { expect } from '@playwright/test';
202
+ ```
203
+
204
+ All fixtures are then available together in any test:
205
+
206
+ ```ts
207
+ test('Authenticated flow', async ({ steps, authService }) => {
208
+ await authService.login('user@test.com', 'secret');
209
+ await steps.verifyUrlContains('/dashboard');
210
+ });
211
+ ```
212
+
213
+ ---
214
+
113
215
  ## 🛠️ API Reference: `Steps`
114
216
 
115
217
  The `Steps` class automatically handles fetching the Playwright `Locator` using your `pageName` and `elementName` keys from the repository.
116
218
 
117
219
  ### 🧭 Navigation
118
220
 
119
- * **`MapsTo(url: string)`**: Navigates the browser to the specified absolute or relative URL.
221
+ * **`navigateTo(url: string)`**: Navigates the browser to the specified absolute or relative URL.
120
222
  * **`refresh()`**: Reloads the current page.
223
+ * **`backOrForward(direction: 'BACKWARDS' | 'FORWARDS')`**: Navigates the browser history stack either backwards or forwards. Mirrors the behavior of the browser's native Back and Forward buttons.
224
+ * **`setViewport(width: number, height: number)`**: Resizes the browser viewport to the specified pixel dimensions. Useful for simulating different device screen sizes or responsive breakpoints.
121
225
 
122
226
  ### 🖱️ Interaction
123
227
 
@@ -169,4 +273,4 @@ await interactions.interact.clickWithoutScrolling(customLocator);
169
273
  await interactions.verify.count(customLocator, { greaterThan: 2 });
170
274
  ```
171
275
 
172
- *Note: All core interaction (`interact`), verification (`verify`), and navigation (`Maps`) methods are also available when using `ElementInteractions` directly.*
276
+ *Note: All core interaction (`interact`), verification (`verify`), and navigation (`navigate`) methods are also available when using `ElementInteractions` directly.*
@@ -0,0 +1,13 @@
1
+ import { ElementInteractions } from '../interactions/facade/ElementInteractions';
2
+ import { ContextStore } from '@civitas-cerebrum/context-store';
3
+ import { ElementRepository } from 'pw-element-repository';
4
+ import { test as base } from '@playwright/test';
5
+ import { Steps } from '../steps/CommonSteps';
6
+ type StepFixture = {
7
+ interactions: ElementInteractions;
8
+ contextStore: ContextStore;
9
+ repo: ElementRepository;
10
+ steps: Steps;
11
+ };
12
+ export declare function baseFixture<T extends {}>(baseTest: ReturnType<typeof base.extend<T>>, locatorPath: string): import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & StepFixture, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
13
+ export {};
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.baseFixture = baseFixture;
4
+ const ElementInteractions_1 = require("../interactions/facade/ElementInteractions");
5
+ const context_store_1 = require("@civitas-cerebrum/context-store");
6
+ const pw_element_repository_1 = require("pw-element-repository");
7
+ const CommonSteps_1 = require("../steps/CommonSteps");
8
+ function baseFixture(baseTest, locatorPath) {
9
+ return baseTest.extend({
10
+ repo: async ({}, use) => {
11
+ await use(new pw_element_repository_1.ElementRepository(locatorPath));
12
+ },
13
+ steps: async ({ page }, use) => {
14
+ const repo = new pw_element_repository_1.ElementRepository(locatorPath);
15
+ await use(new CommonSteps_1.Steps(page, repo));
16
+ },
17
+ interactions: async ({ page }, use) => {
18
+ await use(new ElementInteractions_1.ElementInteractions(page));
19
+ },
20
+ contextStore: async ({}, use) => {
21
+ await use(new context_store_1.ContextStore());
22
+ },
23
+ });
24
+ }
@@ -31,6 +31,19 @@ export declare class Steps {
31
31
  * Reloads the current page.
32
32
  */
33
33
  refresh(): Promise<void>;
34
+ /**
35
+ * Navigates the browser history stack either backwards or forwards.
36
+ * Mirrors the behavior of the browser's native Back and Forward buttons.
37
+ * @param direction - The direction to move in history: either 'BACKWARDS' or 'FORWARDS'.
38
+ */
39
+ backOrForward(direction: 'BACKWARDS' | 'FORWARDS'): Promise<void>;
40
+ /**
41
+ * Resizes the browser viewport to the specified dimensions.
42
+ * Useful for simulating different device screen sizes or responsive breakpoints.
43
+ * @param width - The desired width of the viewport in pixels.
44
+ * @param height - The desired height of the viewport in pixels.
45
+ */
46
+ setViewport(width: number, height: number): Promise<void>;
34
47
  /**
35
48
  * Retrieves an element from the repository and performs a standard click.
36
49
  * @param pageName - The page or component grouping name in your repository.
@@ -50,6 +50,25 @@ class Steps {
50
50
  console.log(`[Step] -> Refreshing the current page`);
51
51
  await this.navigate.reload();
52
52
  }
53
+ /**
54
+ * Navigates the browser history stack either backwards or forwards.
55
+ * Mirrors the behavior of the browser's native Back and Forward buttons.
56
+ * @param direction - The direction to move in history: either 'BACKWARDS' or 'FORWARDS'.
57
+ */
58
+ async backOrForward(direction) {
59
+ console.log(`[Step] -> Navigating browser: "${direction}"`);
60
+ await this.navigate.backOrForward(direction);
61
+ }
62
+ /**
63
+ * Resizes the browser viewport to the specified dimensions.
64
+ * Useful for simulating different device screen sizes or responsive breakpoints.
65
+ * @param width - The desired width of the viewport in pixels.
66
+ * @param height - The desired height of the viewport in pixels.
67
+ */
68
+ async setViewport(width, height) {
69
+ console.log(`[Step] -> Setting viewport to ${width}x${height}`);
70
+ await this.navigate.setViewport(width, height);
71
+ }
53
72
  // ==========================================
54
73
  // 🖱️ INTERACTION STEPS
55
74
  // ==========================================
@@ -15,8 +15,12 @@ export declare class Utils {
15
15
  /**
16
16
  * Standardized wait logic for element states.
17
17
  * Does not fail the test on timeout; logs a warning instead.
18
- * @param locator - The Playwright Locator.
19
- * @param state - The state to wait for.
18
+ * If the locator resolves to multiple elements (strict mode violation),
19
+ * the wait is retried automatically on the first matched element.
20
+ * @param locator - The Playwright Locator to wait on.
21
+ * @param state - The DOM state to wait for. Defaults to `'visible'`.
22
+ * @returns A Promise that resolves when the element reaches the desired state,
23
+ * or silently continues if the timeout is exceeded.
20
24
  */
21
25
  waitForState(locator: Locator, state?: 'visible' | 'attached' | 'hidden' | 'detached'): Promise<void>;
22
26
  }
@@ -21,14 +21,29 @@ class Utils {
21
21
  /**
22
22
  * Standardized wait logic for element states.
23
23
  * Does not fail the test on timeout; logs a warning instead.
24
- * @param locator - The Playwright Locator.
25
- * @param state - The state to wait for.
24
+ * If the locator resolves to multiple elements (strict mode violation),
25
+ * the wait is retried automatically on the first matched element.
26
+ * @param locator - The Playwright Locator to wait on.
27
+ * @param state - The DOM state to wait for. Defaults to `'visible'`.
28
+ * @returns A Promise that resolves when the element reaches the desired state,
29
+ * or silently continues if the timeout is exceeded.
26
30
  */
27
31
  async waitForState(locator, state = 'visible') {
28
32
  try {
29
33
  await locator.waitFor({ state, timeout: this.timeout });
30
34
  }
31
35
  catch (error) {
36
+ const message = error instanceof Error ? error.message : String(error);
37
+ if (message.includes('strict mode violation')) {
38
+ console.warn(`[Warning] -> Locator resolved to multiple elements. Waiting on first element instead.`);
39
+ try {
40
+ await locator.first().waitFor({ state, timeout: this.timeout });
41
+ }
42
+ catch {
43
+ console.warn(`[Warning] -> First element failed to reach state '${state}' within ${this.timeout}ms. Proceeding...`);
44
+ }
45
+ return;
46
+ }
32
47
  console.warn(`[Warning] -> Element failed to reach state '${state}' within ${this.timeout}ms. Proceeding...`);
33
48
  }
34
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pw-element-interactions",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "A robust, readable interaction and assertion Facade for Playwright. Abstract away boilerplate into semantic, English-like methods, making your test automation framework cleaner, easier to maintain, and accessible to non-developers.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,6 +14,7 @@
14
14
  "dist"
15
15
  ],
16
16
  "peerDependencies": {
17
+ "@civitas-cerebrum/context-store": ">=0.0.2",
17
18
  "@playwright/test": ">=1.0.0",
18
19
  "pw-element-repository": ">=0.0.3"
19
20
  },
@@ -44,4 +45,4 @@
44
45
  },
45
46
  "author": "Umut Ay Bora",
46
47
  "license": "MIT"
47
- }
48
+ }