pw-element-interactions 0.0.8 → 0.1.0

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.
@@ -22,174 +22,29 @@ export declare class Steps {
22
22
  * @param timeout - Optional global timeout override (in milliseconds).
23
23
  */
24
24
  constructor(page: Page, repo: ElementRepository, timeout?: number);
25
- /**
26
- * Navigates the browser to the specified URL.
27
- * @param url - The absolute or relative URL to navigate to.
28
- */
29
25
  navigateTo(url: string): Promise<void>;
30
- /**
31
- * Reloads the current page.
32
- */
33
26
  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
27
  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
28
  setViewport(width: number, height: number): Promise<void>;
47
- /**
48
- * Retrieves an element from the repository and performs a standard click.
49
- * @param pageName - The page or component grouping name in your repository.
50
- * @param elementName - The specific element name in your repository.
51
- */
52
29
  click(pageName: string, elementName: string): Promise<void>;
53
- /**
54
- * Retrieves an element and dispatches a native 'click' event directly to it.
55
- * Bypasses default scrolling and intersection checks. Useful for obscured elements.
56
- * @param pageName - The page or component grouping name in your repository.
57
- * @param elementName - The specific element name in your repository.
58
- */
59
30
  clickWithoutScrolling(pageName: string, elementName: string): Promise<void>;
60
- /**
61
- * Retrieves a random element from a resolved list of locators and clicks it.
62
- * Useful for clicking random items in a list or grid (e.g., product cards).
63
- * @param pageName - The page or component grouping name in your repository.
64
- * @param elementName - The specific element name in your repository representing multiple elements.
65
- */
66
31
  clickRandom(pageName: string, elementName: string): Promise<void>;
67
- /**
68
- * Retrieves an element and clicks it only if it is visible.
69
- * Prevents test failures on optional elements like cookie banners or promotional pop-ups.
70
- * @param pageName - The page or component grouping name in your repository.
71
- * @param elementName - The specific element name in your repository.
72
- */
73
32
  clickIfPresent(pageName: string, elementName: string): Promise<void>;
74
- /**
75
- * Retrieves an element and hovers over it. Useful for triggering dropdowns or tooltips.
76
- * @param pageName - The page or component grouping name in your repository.
77
- * @param elementName - The specific element name in your repository.
78
- */
79
33
  hover(pageName: string, elementName: string): Promise<void>;
80
- /**
81
- * Retrieves an element and scrolls it into view if it is not already visible.
82
- * @param pageName - The page or component grouping name in your repository.
83
- * @param elementName - The specific element name in your repository.
84
- */
85
34
  scrollIntoView(pageName: string, elementName: string): Promise<void>;
86
- /**
87
- * Retrieves an input field and fills it with the provided text, replacing any existing value.
88
- * @param pageName - The page or component grouping name in your repository.
89
- * @param elementName - The specific element name in your repository.
90
- * @param text - The text to type into the input field.
91
- */
92
35
  fill(pageName: string, elementName: string, text: string): Promise<void>;
93
- /**
94
- * Retrieves an input element of type `file` and sets its files.
95
- * @param pageName - The page or component grouping name in your repository.
96
- * @param elementName - The specific element name in your repository.
97
- * @param filePath - The local file path of the file to be uploaded.
98
- */
99
36
  uploadFile(pageName: string, elementName: string, filePath: string): Promise<void>;
100
- /**
101
- * Retrieves a `<select>` dropdown element and selects an option based on the provided strategy.
102
- * Defaults to selecting a random, non-disabled option if no strategy is specified.
103
- * @param pageName - The page or component grouping name in your repository.
104
- * @param elementName - The specific element name in your repository.
105
- * @param options - Configuration specifying whether to select by 'random', 'index', or 'value'.
106
- * @returns The exact value attribute of the selected option.
107
- */
108
37
  selectDropdown(pageName: string, elementName: string, options?: DropdownSelectOptions): Promise<string>;
109
- /**
110
- * Drags an element either to a specified target element, a target element with an offset, or by a coordinate offset.
111
- * @param pageName - The page or component grouping name in your repository.
112
- * @param elementName - The specific element name in your repository (the element to drag).
113
- * @param options - Configuration specifying a 'targetLocator', offsets, or both.
114
- */
115
38
  dragAndDrop(pageName: string, elementName: string, options: DragAndDropOptions): Promise<void>;
116
- /**
117
- * Drags an element either to a specified target element, a target element with an offset, or by a coordinate offset.
118
- * @param pageName - The page or component grouping name in your repository.
119
- * @param elementName - The specific element name in your repository (the element to drag).
120
- * @param options - Configuration specifying a 'targetLocator', offsets, or both.
121
- */
122
39
  dragAndDropListedElement(pageName: string, elementName: string, elementText: string, options: DragAndDropOptions): Promise<void>;
123
- /**
124
- * Safely retrieves and trims the text content of a specified element.
125
- * @param pageName - The page or component grouping name in your repository.
126
- * @param elementName - The specific element name in your repository.
127
- * @returns The trimmed string, or an empty string if null.
128
- */
129
40
  getText(pageName: string, elementName: string): Promise<string | null>;
130
- /**
131
- * Retrieves the value of a specified attribute (e.g., 'href', 'aria-pressed') from an element.
132
- * @param pageName - The page or component grouping name in your repository.
133
- * @param elementName - The specific element name in your repository.
134
- * @param attributeName - The name of the attribute to retrieve.
135
- * @returns The attribute value as a string, or null if it doesn't exist.
136
- */
137
41
  getAttribute(pageName: string, elementName: string, attributeName: string): Promise<string | null>;
138
- /**
139
- * Asserts that a specified element is attached to the DOM and is visible.
140
- * @param pageName - The page or component grouping name in your repository.
141
- * @param elementName - The specific element name in your repository.
142
- */
143
42
  verifyPresence(pageName: string, elementName: string): Promise<void>;
144
- /**
145
- * Asserts that a specified element is hidden or completely detached from the DOM.
146
- * @param pageName - The page or component grouping name in your repository.
147
- * @param elementName - The specific element name in your repository.
148
- */
149
43
  verifyAbsence(pageName: string, elementName: string): Promise<void>;
150
- /**
151
- * Asserts that the specified element exactly matches the expected text, or optionally checks if it is not empty.
152
- * @param pageName - The page or component grouping name in your repository.
153
- * @param elementName - The specific element name in your repository.
154
- * @param expectedText - The exact string expected inside the element (optional if checking 'notEmpty').
155
- * @param options - Configuration to alter the verification behavior.
156
- */
157
44
  verifyText(pageName: string, elementName: string, expectedText?: string, options?: TextVerifyOptions): Promise<void>;
158
- /**
159
- * Asserts the number of elements matching the locator based on the provided conditions.
160
- * @param pageName - The page or component grouping name in your repository.
161
- * @param elementName - The specific element name in your repository.
162
- * @param options - Configuration specifying 'exact', 'greaterThan', or 'lessThan' logic.
163
- */
164
45
  verifyCount(pageName: string, elementName: string, options: CountVerifyOptions): Promise<void>;
165
- /**
166
- * Performs a rigorous verification of one or more images.
167
- * Asserts visibility, checks for a valid 'src' attribute, ensures a positive 'naturalWidth',
168
- * and evaluates the native browser `decode()` promise to ensure the image isn't broken.
169
- * @param pageName - The page or component grouping name in your repository.
170
- * @param elementName - The specific element name in your repository.
171
- * @param scroll - Whether to smoothly scroll the image(s) into view before verifying (default: true).
172
- */
173
46
  verifyImages(pageName: string, elementName: string, scroll?: boolean): Promise<void>;
174
- /**
175
- * Asserts that the current browser URL contains the expected substring.
176
- * @param text - The substring expected to be present within the active URL.
177
- */
178
47
  verifyUrlContains(text: string): Promise<void>;
179
- /**
180
- * Waits for an element to reach a specific state in the DOM.
181
- * @param pageName - The page or component grouping name in your repository.
182
- * @param elementName - The specific element name in your repository.
183
- * @param state - The state to wait for: 'visible' | 'attached' | 'hidden' | 'detached'. Defaults to 'visible'.
184
- */
185
48
  waitForState(pageName: string, elementName: string, state?: 'visible' | 'attached' | 'hidden' | 'detached'): Promise<void>;
186
- /**
187
- * Types text into a specific element character by character with a delay.
188
- * Ideal for OTP inputs, search bars, or fields with sensitive 'keyup' listeners.
189
- * @param pageName - The page or component grouping name in your repository.
190
- * @param elementName - The specific element name in your repository.
191
- * @param text - The string to type sequentially.
192
- * @param delay - Optional delay between key presses in milliseconds (defaults to 100).
193
- */
194
49
  typeSequentially(pageName: string, elementName: string, text: string, delay?: number): Promise<void>;
195
50
  }
@@ -2,6 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Steps = void 0;
4
4
  const ElementInteractions_1 = require("../interactions/facade/ElementInteractions");
5
+ const Logger_1 = require("../logger/Logger");
6
+ const logger = (type) => (0, Logger_1.createLogger)(`${type}`);
7
+ const log = {
8
+ navigate: logger('navigate'),
9
+ interact: logger('interact'),
10
+ extract: logger('extract'),
11
+ verify: logger('verify'),
12
+ wait: logger('wait'),
13
+ };
5
14
  /**
6
15
  * The `Steps` class serves as a unified Facade for test orchestration.
7
16
  * It combines element acquisition (via `pw-element-repository`) with
@@ -35,279 +44,136 @@ class Steps {
35
44
  // ==========================================
36
45
  // 🧭 NAVIGATION STEPS
37
46
  // ==========================================
38
- /**
39
- * Navigates the browser to the specified URL.
40
- * @param url - The absolute or relative URL to navigate to.
41
- */
42
47
  async navigateTo(url) {
43
- console.log(`[Step] -> Navigating to URL: "${url}"`);
48
+ log.navigate('Navigating to URL: "%s"', url);
44
49
  await this.navigate.toUrl(url);
45
50
  }
46
- /**
47
- * Reloads the current page.
48
- */
49
51
  async refresh() {
50
- console.log(`[Step] -> Refreshing the current page`);
52
+ log.navigate('Refreshing the current page');
51
53
  await this.navigate.reload();
52
54
  }
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
55
  async backOrForward(direction) {
59
- console.log(`[Step] -> Navigating browser: "${direction}"`);
56
+ log.navigate('Navigating browser: "%s"', direction);
60
57
  await this.navigate.backOrForward(direction);
61
58
  }
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
59
  async setViewport(width, height) {
69
- console.log(`[Step] -> Setting viewport to ${width}x${height}`);
60
+ log.navigate('Setting viewport to %dx%d', width, height);
70
61
  await this.navigate.setViewport(width, height);
71
62
  }
72
63
  // ==========================================
73
64
  // 🖱️ INTERACTION STEPS
74
65
  // ==========================================
75
- /**
76
- * Retrieves an element from the repository and performs a standard click.
77
- * @param pageName - The page or component grouping name in your repository.
78
- * @param elementName - The specific element name in your repository.
79
- */
80
66
  async click(pageName, elementName) {
81
- console.log(`[Step] -> Clicking on '${elementName}' in '${pageName}'`);
67
+ log.interact('Clicking on "%s" in "%s"', elementName, pageName);
82
68
  const locator = await this.repo.get(this.page, pageName, elementName);
83
69
  await this.interact.click(locator);
84
70
  }
85
- /**
86
- * Retrieves an element and dispatches a native 'click' event directly to it.
87
- * Bypasses default scrolling and intersection checks. Useful for obscured elements.
88
- * @param pageName - The page or component grouping name in your repository.
89
- * @param elementName - The specific element name in your repository.
90
- */
91
71
  async clickWithoutScrolling(pageName, elementName) {
92
- console.log(`[Step] -> Clicking (no scroll) on '${elementName}' in '${pageName}'`);
72
+ log.interact('Clicking (no scroll) on "%s" in "%s"', elementName, pageName);
93
73
  const locator = await this.repo.get(this.page, pageName, elementName);
94
74
  await this.interact.clickWithoutScrolling(locator);
95
75
  }
96
- /**
97
- * Retrieves a random element from a resolved list of locators and clicks it.
98
- * Useful for clicking random items in a list or grid (e.g., product cards).
99
- * @param pageName - The page or component grouping name in your repository.
100
- * @param elementName - The specific element name in your repository representing multiple elements.
101
- */
102
76
  async clickRandom(pageName, elementName) {
103
- console.log(`[Step] -> Clicking a random element from '${elementName}' in '${pageName}'`);
77
+ log.interact('Clicking a random element from "%s" in "%s"', elementName, pageName);
104
78
  const locator = await this.repo.getRandom(this.page, pageName, elementName);
105
79
  await this.interact.click(locator);
106
80
  }
107
- /**
108
- * Retrieves an element and clicks it only if it is visible.
109
- * Prevents test failures on optional elements like cookie banners or promotional pop-ups.
110
- * @param pageName - The page or component grouping name in your repository.
111
- * @param elementName - The specific element name in your repository.
112
- */
113
81
  async clickIfPresent(pageName, elementName) {
114
- console.log(`[Step] -> Clicking on '${elementName}' in '${pageName}' (if present)`);
82
+ log.interact('Clicking on "%s" in "%s" (if present)', elementName, pageName);
115
83
  const locator = await this.repo.get(this.page, pageName, elementName);
116
84
  await this.interact.clickIfPresent(locator);
117
85
  }
118
- /**
119
- * Retrieves an element and hovers over it. Useful for triggering dropdowns or tooltips.
120
- * @param pageName - The page or component grouping name in your repository.
121
- * @param elementName - The specific element name in your repository.
122
- */
123
86
  async hover(pageName, elementName) {
124
- console.log(`[Step] -> Hovering over '${elementName}' in '${pageName}'`);
87
+ log.interact('Hovering over "%s" in "%s"', elementName, pageName);
125
88
  const locator = await this.repo.get(this.page, pageName, elementName);
126
89
  await this.interact.hover(locator);
127
90
  }
128
- /**
129
- * Retrieves an element and scrolls it into view if it is not already visible.
130
- * @param pageName - The page or component grouping name in your repository.
131
- * @param elementName - The specific element name in your repository.
132
- */
133
91
  async scrollIntoView(pageName, elementName) {
134
- console.log(`[Step] -> Scrolling '${elementName}' in '${pageName}' into view`);
92
+ log.interact('Scrolling "%s" in "%s" into view', elementName, pageName);
135
93
  const locator = await this.repo.get(this.page, pageName, elementName);
136
94
  await this.interact.scrollIntoView(locator);
137
95
  }
138
- /**
139
- * Retrieves an input field and fills it with the provided text, replacing any existing value.
140
- * @param pageName - The page or component grouping name in your repository.
141
- * @param elementName - The specific element name in your repository.
142
- * @param text - The text to type into the input field.
143
- */
144
96
  async fill(pageName, elementName, text) {
145
- console.log(`[Step] -> Filling '${elementName}' in '${pageName}' with text: "${text}"`);
97
+ log.interact('Filling "%s" in "%s" with text: "%s"', elementName, pageName, text);
146
98
  const locator = await this.repo.get(this.page, pageName, elementName);
147
99
  await this.interact.fill(locator, text);
148
100
  }
149
- /**
150
- * Retrieves an input element of type `file` and sets its files.
151
- * @param pageName - The page or component grouping name in your repository.
152
- * @param elementName - The specific element name in your repository.
153
- * @param filePath - The local file path of the file to be uploaded.
154
- */
155
101
  async uploadFile(pageName, elementName, filePath) {
156
- console.log(`[Step] -> Uploading file "${filePath}" to '${elementName}' in '${pageName}'`);
102
+ log.interact('Uploading file "%s" to "%s" in "%s"', filePath, elementName, pageName);
157
103
  const locator = await this.repo.get(this.page, pageName, elementName);
158
104
  await this.interact.uploadFile(locator, filePath);
159
105
  }
160
- /**
161
- * Retrieves a `<select>` dropdown element and selects an option based on the provided strategy.
162
- * Defaults to selecting a random, non-disabled option if no strategy is specified.
163
- * @param pageName - The page or component grouping name in your repository.
164
- * @param elementName - The specific element name in your repository.
165
- * @param options - Configuration specifying whether to select by 'random', 'index', or 'value'.
166
- * @returns The exact value attribute of the selected option.
167
- */
168
106
  async selectDropdown(pageName, elementName, options) {
169
- const optLog = options ? JSON.stringify(options) : 'default (random)';
170
- console.log(`[Step] -> Selecting dropdown option for '${elementName}' in '${pageName}' using options: ${optLog}`);
107
+ log.interact('Selecting dropdown option for "%s" in "%s" using options: %O', elementName, pageName, options ?? 'default (random)');
171
108
  const locator = await this.repo.get(this.page, pageName, elementName);
172
109
  return await this.interact.selectDropdown(locator, options);
173
110
  }
174
- /**
175
- * Drags an element either to a specified target element, a target element with an offset, or by a coordinate offset.
176
- * @param pageName - The page or component grouping name in your repository.
177
- * @param elementName - The specific element name in your repository (the element to drag).
178
- * @param options - Configuration specifying a 'targetLocator', offsets, or both.
179
- */
180
111
  async dragAndDrop(pageName, elementName, options) {
181
- console.log(`[Step] -> Dragging and dropping '${elementName}' in '${pageName}'`);
112
+ log.interact('Dragging and dropping "%s" in "%s"', elementName, pageName);
182
113
  const locator = await this.repo.get(this.page, pageName, elementName);
183
114
  await this.interact.dragAndDrop(locator, options);
184
115
  }
185
- /**
186
- * Drags an element either to a specified target element, a target element with an offset, or by a coordinate offset.
187
- * @param pageName - The page or component grouping name in your repository.
188
- * @param elementName - The specific element name in your repository (the element to drag).
189
- * @param options - Configuration specifying a 'targetLocator', offsets, or both.
190
- */
191
116
  async dragAndDropListedElement(pageName, elementName, elementText, options) {
192
- console.log(`[Step] -> Dragging and dropping '${elementText}' in '${pageName}'`);
117
+ log.interact('Dragging and dropping "%s" in "%s"', elementText, pageName);
193
118
  const locator = await this.repo.getByText(this.page, pageName, elementName, elementText);
194
119
  await this.interact.dragAndDrop(locator, options);
195
120
  }
196
121
  // ==========================================
197
122
  // 📊 DATA EXTRACTION STEPS
198
123
  // ==========================================
199
- /**
200
- * Safely retrieves and trims the text content of a specified element.
201
- * @param pageName - The page or component grouping name in your repository.
202
- * @param elementName - The specific element name in your repository.
203
- * @returns The trimmed string, or an empty string if null.
204
- */
205
124
  async getText(pageName, elementName) {
206
- console.log(`[Step] -> Getting text from '${elementName}' in '${pageName}'`);
125
+ log.extract('Getting text from "%s" in "%s"', elementName, pageName);
207
126
  const locator = await this.repo.get(this.page, pageName, elementName);
208
127
  return await this.extract.getText(locator);
209
128
  }
210
- /**
211
- * Retrieves the value of a specified attribute (e.g., 'href', 'aria-pressed') from an element.
212
- * @param pageName - The page or component grouping name in your repository.
213
- * @param elementName - The specific element name in your repository.
214
- * @param attributeName - The name of the attribute to retrieve.
215
- * @returns The attribute value as a string, or null if it doesn't exist.
216
- */
217
129
  async getAttribute(pageName, elementName, attributeName) {
218
- console.log(`[Step] -> Getting attribute '${attributeName}' from '${elementName}' in '${pageName}'`);
130
+ log.extract('Getting attribute "%s" from "%s" in "%s"', attributeName, elementName, pageName);
219
131
  const locator = await this.repo.get(this.page, pageName, elementName);
220
132
  return await this.extract.getAttribute(locator, attributeName);
221
133
  }
222
134
  // ==========================================
223
135
  // ✅ VERIFICATION STEPS
224
136
  // ==========================================
225
- /**
226
- * Asserts that a specified element is attached to the DOM and is visible.
227
- * @param pageName - The page or component grouping name in your repository.
228
- * @param elementName - The specific element name in your repository.
229
- */
230
137
  async verifyPresence(pageName, elementName) {
231
- console.log(`[Step] -> Verifying presence of '${elementName}' in '${pageName}'`);
138
+ log.verify('Verifying presence of "%s" in "%s"', elementName, pageName);
232
139
  const locator = await this.repo.get(this.page, pageName, elementName);
233
140
  await this.verify.presence(locator);
234
141
  }
235
- /**
236
- * Asserts that a specified element is hidden or completely detached from the DOM.
237
- * @param pageName - The page or component grouping name in your repository.
238
- * @param elementName - The specific element name in your repository.
239
- */
240
142
  async verifyAbsence(pageName, elementName) {
241
- console.log(`[Step] -> Verifying absence of '${elementName}' in '${pageName}'`);
143
+ log.verify('Verifying absence of "%s" in "%s"', elementName, pageName);
242
144
  const selector = await this.repo.getSelector(pageName, elementName);
243
145
  await this.verify.absence(selector);
244
146
  }
245
- /**
246
- * Asserts that the specified element exactly matches the expected text, or optionally checks if it is not empty.
247
- * @param pageName - The page or component grouping name in your repository.
248
- * @param elementName - The specific element name in your repository.
249
- * @param expectedText - The exact string expected inside the element (optional if checking 'notEmpty').
250
- * @param options - Configuration to alter the verification behavior.
251
- */
252
147
  async verifyText(pageName, elementName, expectedText, options) {
253
- const logDetail = options?.notEmpty ? `is not empty` : `matches: "${expectedText}"`;
254
- console.log(`[Step] -> Verifying text of '${elementName}' in '${pageName}' ${logDetail}`);
148
+ const logDetail = options?.notEmpty ? 'is not empty' : `matches: "${expectedText}"`;
149
+ log.verify('Verifying text of "%s" in "%s" %s', elementName, pageName, logDetail);
255
150
  const locator = await this.repo.get(this.page, pageName, elementName);
256
151
  await this.verify.text(locator, expectedText, options);
257
152
  }
258
- /**
259
- * Asserts the number of elements matching the locator based on the provided conditions.
260
- * @param pageName - The page or component grouping name in your repository.
261
- * @param elementName - The specific element name in your repository.
262
- * @param options - Configuration specifying 'exact', 'greaterThan', or 'lessThan' logic.
263
- */
264
153
  async verifyCount(pageName, elementName, options) {
265
- console.log(`[Step] -> Verifying count for '${elementName}' in '${pageName}' with options: ${JSON.stringify(options)}`);
154
+ log.verify('Verifying count for "%s" in "%s" with options: %O', elementName, pageName, options);
266
155
  const locator = await this.repo.get(this.page, pageName, elementName);
267
156
  await this.verify.count(locator, options);
268
157
  }
269
- /**
270
- * Performs a rigorous verification of one or more images.
271
- * Asserts visibility, checks for a valid 'src' attribute, ensures a positive 'naturalWidth',
272
- * and evaluates the native browser `decode()` promise to ensure the image isn't broken.
273
- * @param pageName - The page or component grouping name in your repository.
274
- * @param elementName - The specific element name in your repository.
275
- * @param scroll - Whether to smoothly scroll the image(s) into view before verifying (default: true).
276
- */
277
158
  async verifyImages(pageName, elementName, scroll = true) {
278
- console.log(`[Step] -> Verifying images for '${elementName}' in '${pageName}' (Scroll: ${scroll})`);
159
+ log.verify('Verifying images for "%s" in "%s" (scroll: %s)', elementName, pageName, scroll);
279
160
  const locator = await this.repo.get(this.page, pageName, elementName);
280
161
  await this.verify.images(locator, scroll);
281
162
  }
282
- /**
283
- * Asserts that the current browser URL contains the expected substring.
284
- * @param text - The substring expected to be present within the active URL.
285
- */
286
163
  async verifyUrlContains(text) {
287
- console.log(`[Step] -> Verifying current URL contains: "${text}"`);
164
+ log.verify('Verifying current URL contains: "%s"', text);
288
165
  await this.verify.urlContains(text);
289
166
  }
290
- /**
291
- * Waits for an element to reach a specific state in the DOM.
292
- * @param pageName - The page or component grouping name in your repository.
293
- * @param elementName - The specific element name in your repository.
294
- * @param state - The state to wait for: 'visible' | 'attached' | 'hidden' | 'detached'. Defaults to 'visible'.
295
- */
167
+ // ==========================================
168
+ // WAIT STEPS
169
+ // ==========================================
296
170
  async waitForState(pageName, elementName, state = 'visible') {
297
- console.log(`[Step] -> Waiting for '${elementName}' in '${pageName}' to be '${state}'`);
171
+ log.wait('Waiting for "%s" in "%s" to be "%s"', elementName, pageName, state);
298
172
  const locator = await this.repo.get(this.page, pageName, elementName);
299
173
  await this.utils.waitForState(locator, state);
300
174
  }
301
- /**
302
- * Types text into a specific element character by character with a delay.
303
- * Ideal for OTP inputs, search bars, or fields with sensitive 'keyup' listeners.
304
- * @param pageName - The page or component grouping name in your repository.
305
- * @param elementName - The specific element name in your repository.
306
- * @param text - The string to type sequentially.
307
- * @param delay - Optional delay between key presses in milliseconds (defaults to 100).
308
- */
309
175
  async typeSequentially(pageName, elementName, text, delay = 100) {
310
- console.log(`[Step] -> Typing "${text}" sequentially into '${elementName}' in '${pageName}' (Delay: ${delay}ms)`);
176
+ log.interact('Typing "%s" sequentially into "%s" in "%s" (delay: %dms)', text, elementName, pageName, delay);
311
177
  const locator = await this.repo.get(this.page, pageName, elementName);
312
178
  await this.interact.typeSequentially(locator, text, delay);
313
179
  }
@@ -35,16 +35,16 @@ class Utils {
35
35
  catch (error) {
36
36
  const message = error instanceof Error ? error.message : String(error);
37
37
  if (message.includes('strict mode violation')) {
38
- console.warn(`[Warning] -> Locator resolved to multiple elements. Waiting on first element instead.`);
38
+ console.warn(`Locator resolved to multiple elements. Waiting on first element instead.`);
39
39
  try {
40
40
  await locator.first().waitFor({ state, timeout: this.timeout });
41
41
  }
42
42
  catch {
43
- console.warn(`[Warning] -> First element failed to reach state '${state}' within ${this.timeout}ms. Proceeding...`);
43
+ console.warn(`First element failed to reach state '${state}' within ${this.timeout}ms. Proceeding...`);
44
44
  }
45
45
  return;
46
46
  }
47
- console.warn(`[Warning] -> Element failed to reach state '${state}' within ${this.timeout}ms. Proceeding...`);
47
+ console.warn(`Element failed to reach state '${state}' within ${this.timeout}ms. Proceeding...`);
48
48
  }
49
49
  }
50
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pw-element-interactions",
3
- "version": "0.0.8",
3
+ "version": "0.1.0",
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",
@@ -8,10 +8,14 @@
8
8
  "clean": "rm -rf dist",
9
9
  "build": "npm run clean && tsc",
10
10
  "test": "npx playwright test",
11
- "prepublishOnly": "npm run build"
11
+ "prepublishOnly": "npm run build",
12
+ "postinstall": "node scripts/postinstall.js",
13
+ "test:quiet": "TESTER_DEBUG=false npx playwright test"
12
14
  },
13
15
  "files": [
14
- "dist"
16
+ "dist",
17
+ "skills",
18
+ "scripts/postinstall.js"
15
19
  ],
16
20
  "peerDependencies": {
17
21
  "@civitas-cerebrum/context-store": ">=0.0.2",
@@ -21,6 +25,7 @@
21
25
  "devDependencies": {
22
26
  "@civitas-cerebrum/context-store": "^0.0.2",
23
27
  "@playwright/test": "^1.58.2",
28
+ "@types/debug": "^4.1.13",
24
29
  "@types/node": "^20.0.0",
25
30
  "pw-element-repository": "^0.0.3",
26
31
  "typescript": "^5.0.0"
@@ -44,5 +49,8 @@
44
49
  "url": "git+https://github.com/Umutayb/pw-element-interactions.git"
45
50
  },
46
51
  "author": "Umut Ay Bora",
47
- "license": "MIT"
52
+ "license": "MIT",
53
+ "dependencies": {
54
+ "debug": "^4.4.3"
55
+ }
48
56
  }
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ // Resolve the root of the consuming project (three levels up from
7
+ // node_modules/pw-element-interactions/scripts/postinstall.js)
8
+ const projectRoot = path.resolve(__dirname, '..', '..', '..');
9
+
10
+ const src = path.join(__dirname, '..', 'skills', 'pw-element-interactions.md');
11
+ const dest = path.join(projectRoot, '.claude', 'skills', 'pw-element-interactions', 'SKILL.md');
12
+
13
+ try {
14
+ if (!fs.existsSync(src)) {
15
+ console.warn('[pw-element-interactions] Skill file not found, skipping.');
16
+ process.exit(0);
17
+ }
18
+
19
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
20
+ fs.copyFileSync(src, dest);
21
+ console.log('[pw-element-interactions] ✔ Claude Code skill installed — restart Claude Code to pick it up.');
22
+ } catch (err) {
23
+ // Never fail the install — skill copy is best-effort
24
+ console.warn(`[pw-element-interactions] Could not install Claude Code skill: ${err.message}`);
25
+ }