browser-commander 0.8.1 → 0.9.1

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.
@@ -13,6 +13,12 @@
13
13
 
14
14
  import { TIMING } from './constants.js';
15
15
 
16
+ function notImplemented(methodName) {
17
+ return Promise.reject(
18
+ new Error(`${methodName}() must be implemented by subclass`)
19
+ );
20
+ }
21
+
16
22
  /**
17
23
  * Base class defining the engine adapter interface
18
24
  * All engine-specific operations should be defined here
@@ -39,7 +45,7 @@ export class EngineAdapter {
39
45
  * @param {string} selector - CSS selector
40
46
  * @returns {Object} - Locator (Playwright) or ElementHandle (Puppeteer)
41
47
  */
42
- createLocator(selector) {
48
+ createLocator(_selector) {
43
49
  throw new Error('createLocator() must be implemented by subclass');
44
50
  }
45
51
 
@@ -48,8 +54,8 @@ export class EngineAdapter {
48
54
  * @param {string} selector - CSS selector
49
55
  * @returns {Promise<Object|null>} - Locator/Element or null
50
56
  */
51
- async querySelector(selector) {
52
- throw new Error('querySelector() must be implemented by subclass');
57
+ querySelector(_selector) {
58
+ return notImplemented('querySelector');
53
59
  }
54
60
 
55
61
  /**
@@ -57,8 +63,8 @@ export class EngineAdapter {
57
63
  * @param {string} selector - CSS selector
58
64
  * @returns {Promise<Array>} - Array of locators/elements
59
65
  */
60
- async querySelectorAll(selector) {
61
- throw new Error('querySelectorAll() must be implemented by subclass');
66
+ querySelectorAll(_selector) {
67
+ return notImplemented('querySelectorAll');
62
68
  }
63
69
 
64
70
  /**
@@ -67,8 +73,8 @@ export class EngineAdapter {
67
73
  * @param {Object} options - Wait options {visible, timeout}
68
74
  * @returns {Promise<void>}
69
75
  */
70
- async waitForSelector(selector, options = {}) {
71
- throw new Error('waitForSelector() must be implemented by subclass');
76
+ waitForSelector(_selector, _options = {}) {
77
+ return notImplemented('waitForSelector');
72
78
  }
73
79
 
74
80
  /**
@@ -77,8 +83,8 @@ export class EngineAdapter {
77
83
  * @param {number} timeout - Timeout in ms
78
84
  * @returns {Promise<Object>} - The locator/element
79
85
  */
80
- async waitForVisible(locatorOrElement, timeout = TIMING.DEFAULT_TIMEOUT) {
81
- throw new Error('waitForVisible() must be implemented by subclass');
86
+ waitForVisible(_locatorOrElement, _timeout = TIMING.DEFAULT_TIMEOUT) {
87
+ return notImplemented('waitForVisible');
82
88
  }
83
89
 
84
90
  /**
@@ -86,8 +92,8 @@ export class EngineAdapter {
86
92
  * @param {string} selector - CSS selector
87
93
  * @returns {Promise<number>} - Number of matching elements
88
94
  */
89
- async count(selector) {
90
- throw new Error('count() must be implemented by subclass');
95
+ count(_selector) {
96
+ return notImplemented('count');
91
97
  }
92
98
 
93
99
  // ============================================================================
@@ -101,8 +107,8 @@ export class EngineAdapter {
101
107
  * @param {any} args - Arguments to pass (optional)
102
108
  * @returns {Promise<any>} - Result of evaluation
103
109
  */
104
- async evaluateOnElement(locatorOrElement, fn, args) {
105
- throw new Error('evaluateOnElement() must be implemented by subclass');
110
+ evaluateOnElement(_locatorOrElement, _fn, _args) {
111
+ return notImplemented('evaluateOnElement');
106
112
  }
107
113
 
108
114
  /**
@@ -110,8 +116,8 @@ export class EngineAdapter {
110
116
  * @param {Object} locatorOrElement - Locator or element
111
117
  * @returns {Promise<string|null>} - Text content
112
118
  */
113
- async getTextContent(locatorOrElement) {
114
- throw new Error('getTextContent() must be implemented by subclass');
119
+ getTextContent(_locatorOrElement) {
120
+ return notImplemented('getTextContent');
115
121
  }
116
122
 
117
123
  /**
@@ -119,8 +125,8 @@ export class EngineAdapter {
119
125
  * @param {Object} locatorOrElement - Locator or element
120
126
  * @returns {Promise<string>} - Input value
121
127
  */
122
- async getInputValue(locatorOrElement) {
123
- throw new Error('getInputValue() must be implemented by subclass');
128
+ getInputValue(_locatorOrElement) {
129
+ return notImplemented('getInputValue');
124
130
  }
125
131
 
126
132
  /**
@@ -129,8 +135,8 @@ export class EngineAdapter {
129
135
  * @param {string} attribute - Attribute name
130
136
  * @returns {Promise<string|null>} - Attribute value
131
137
  */
132
- async getAttribute(locatorOrElement, attribute) {
133
- throw new Error('getAttribute() must be implemented by subclass');
138
+ getAttribute(_locatorOrElement, _attribute) {
139
+ return notImplemented('getAttribute');
134
140
  }
135
141
 
136
142
  // ============================================================================
@@ -143,8 +149,8 @@ export class EngineAdapter {
143
149
  * @param {Object} options - Click options {force, etc.}
144
150
  * @returns {Promise<void>}
145
151
  */
146
- async click(locatorOrElement, options = {}) {
147
- throw new Error('click() must be implemented by subclass');
152
+ click(_locatorOrElement, _options = {}) {
153
+ return notImplemented('click');
148
154
  }
149
155
 
150
156
  /**
@@ -153,8 +159,8 @@ export class EngineAdapter {
153
159
  * @param {string} text - Text to type
154
160
  * @returns {Promise<void>}
155
161
  */
156
- async type(locatorOrElement, text) {
157
- throw new Error('type() must be implemented by subclass');
162
+ type(_locatorOrElement, _text) {
163
+ return notImplemented('type');
158
164
  }
159
165
 
160
166
  /**
@@ -163,8 +169,8 @@ export class EngineAdapter {
163
169
  * @param {string} text - Text to fill
164
170
  * @returns {Promise<void>}
165
171
  */
166
- async fill(locatorOrElement, text) {
167
- throw new Error('fill() must be implemented by subclass');
172
+ fill(_locatorOrElement, _text) {
173
+ return notImplemented('fill');
168
174
  }
169
175
 
170
176
  /**
@@ -172,8 +178,8 @@ export class EngineAdapter {
172
178
  * @param {Object} locatorOrElement - Locator or element
173
179
  * @returns {Promise<void>}
174
180
  */
175
- async focus(locatorOrElement) {
176
- throw new Error('focus() must be implemented by subclass');
181
+ focus(_locatorOrElement) {
182
+ return notImplemented('focus');
177
183
  }
178
184
 
179
185
  // ============================================================================
@@ -185,8 +191,8 @@ export class EngineAdapter {
185
191
  * @param {string} key - Key name (Playwright/Puppeteer key format)
186
192
  * @returns {Promise<void>}
187
193
  */
188
- async keyboardPress(key) {
189
- throw new Error('keyboardPress() must be implemented by subclass');
194
+ keyboardPress(_key) {
195
+ return notImplemented('keyboardPress');
190
196
  }
191
197
 
192
198
  /**
@@ -194,8 +200,8 @@ export class EngineAdapter {
194
200
  * @param {string} text - Text to type
195
201
  * @returns {Promise<void>}
196
202
  */
197
- async keyboardType(text) {
198
- throw new Error('keyboardType() must be implemented by subclass');
203
+ keyboardType(_text) {
204
+ return notImplemented('keyboardType');
199
205
  }
200
206
 
201
207
  /**
@@ -203,8 +209,8 @@ export class EngineAdapter {
203
209
  * @param {string} key - Key name
204
210
  * @returns {Promise<void>}
205
211
  */
206
- async keyboardDown(key) {
207
- throw new Error('keyboardDown() must be implemented by subclass');
212
+ keyboardDown(_key) {
213
+ return notImplemented('keyboardDown');
208
214
  }
209
215
 
210
216
  /**
@@ -212,8 +218,8 @@ export class EngineAdapter {
212
218
  * @param {string} key - Key name
213
219
  * @returns {Promise<void>}
214
220
  */
215
- async keyboardUp(key) {
216
- throw new Error('keyboardUp() must be implemented by subclass');
221
+ keyboardUp(_key) {
222
+ return notImplemented('keyboardUp');
217
223
  }
218
224
 
219
225
  // ============================================================================
@@ -226,8 +232,8 @@ export class EngineAdapter {
226
232
  * @param {Array} args - Arguments to pass
227
233
  * @returns {Promise<any>} - Result of evaluation
228
234
  */
229
- async evaluateOnPage(fn, args = []) {
230
- throw new Error('evaluateOnPage() must be implemented by subclass');
235
+ evaluateOnPage(_fn, _args = []) {
236
+ return notImplemented('evaluateOnPage');
231
237
  }
232
238
 
233
239
  /**
@@ -243,8 +249,8 @@ export class EngineAdapter {
243
249
  * @param {Object} options - PDF options (format, printBackground, margin, etc.)
244
250
  * @returns {Promise<Buffer>} - PDF buffer
245
251
  */
246
- async pdf(options = {}) {
247
- throw new Error('pdf() must be implemented by subclass');
252
+ pdf(_options = {}) {
253
+ return notImplemented('pdf');
248
254
  }
249
255
  }
250
256
 
@@ -435,7 +441,7 @@ export class PuppeteerAdapter extends EngineAdapter {
435
441
  await this.page.waitForSelector(selector, { visible, timeout });
436
442
  }
437
443
 
438
- async waitForVisible(locatorOrElement, timeout = TIMING.DEFAULT_TIMEOUT) {
444
+ waitForVisible(locatorOrElement, _timeout = TIMING.DEFAULT_TIMEOUT) {
439
445
  // For Puppeteer, locatorOrElement is already an ElementHandle
440
446
  // We can't wait on it directly, so we just return it
441
447
  // The caller should have already used waitForSelector
@@ -370,7 +370,7 @@ export function createNavigationManager(options = {}) {
370
370
  });
371
371
  }
372
372
 
373
- return navigationPromise;
373
+ return await navigationPromise;
374
374
  }
375
375
 
376
376
  /**
@@ -209,7 +209,6 @@ export function createNetworkTracker(options = {}) {
209
209
  async function waitForNetworkIdle(opts = {}) {
210
210
  const { timeout = 30000, idleTime = idleTimeout } = opts;
211
211
 
212
- const startTime = Date.now();
213
212
  const currentNavId = navigationId;
214
213
 
215
214
  // If already idle, wait for idle time
@@ -317,7 +316,7 @@ export function createNetworkTracker(options = {}) {
317
316
  */
318
317
  function getPendingUrls() {
319
318
  const urls = [];
320
- for (const [key, req] of pendingRequests) {
319
+ for (const req of pendingRequests.values()) {
321
320
  const url = typeof req.url === 'function' ? req.url() : req.url;
322
321
  urls.push(url);
323
322
  }
@@ -65,7 +65,7 @@ export function createPageSessionFactory(options = {}) {
65
65
  /**
66
66
  * Handle navigation - deactivate session if URL no longer matches
67
67
  */
68
- function handleUrlChange({ previousUrl, newUrl }) {
68
+ function handleUrlChange({ newUrl }) {
69
69
  if (!isActive) {
70
70
  return;
71
71
  }
@@ -115,7 +115,7 @@ export function createPageSessionFactory(options = {}) {
115
115
  for (const { target, event, handler } of eventListeners) {
116
116
  try {
117
117
  target.off(event, handler);
118
- } catch (e) {
118
+ } catch {
119
119
  // Ignore removal errors
120
120
  }
121
121
  }
@@ -166,7 +166,7 @@ export function createPageSessionFactory(options = {}) {
166
166
  log.debug(() => `⏭️ Skipping action - session "${name}" is inactive`);
167
167
  return null;
168
168
  }
169
- return fn();
169
+ return await fn();
170
170
  }
171
171
 
172
172
  /**
@@ -214,7 +214,7 @@ export function createPageSessionFactory(options = {}) {
214
214
  if (!networkTracker) {
215
215
  return true;
216
216
  }
217
- return networkTracker.waitForNetworkIdle(opts);
217
+ return await networkTracker.waitForNetworkIdle(opts);
218
218
  }
219
219
 
220
220
  /**
@@ -224,7 +224,7 @@ export function createPageSessionFactory(options = {}) {
224
224
  if (!isActive) {
225
225
  return false;
226
226
  }
227
- return navigationManager.waitForPageReady(opts);
227
+ return await navigationManager.waitForPageReady(opts);
228
228
  }
229
229
 
230
230
  /**
@@ -310,7 +310,7 @@ export function createPageTriggerManager(options = {}) {
310
310
  * @param {string} url - URL to start action for
311
311
  * @param {Object} commander - BrowserCommander instance
312
312
  */
313
- async function startAction(url, commander) {
313
+ function startAction(url, commander) {
314
314
  // Create context for condition checking
315
315
  const conditionCtx = {
316
316
  url,
@@ -321,7 +321,7 @@ export function createPageTriggerManager(options = {}) {
321
321
  const matchingTrigger = findMatchingTrigger(conditionCtx);
322
322
  if (!matchingTrigger) {
323
323
  log.debug(() => `📋 No trigger registered for: ${url}`);
324
- return;
324
+ return Promise.resolve();
325
325
  }
326
326
 
327
327
  log.debug(() => `🚀 Starting action "${matchingTrigger.name}" for: ${url}`);
@@ -369,6 +369,8 @@ export function createPageTriggerManager(options = {}) {
369
369
  }
370
370
  }
371
371
  })();
372
+
373
+ return Promise.resolve();
372
374
  }
373
375
 
374
376
  /**
@@ -92,16 +92,16 @@ export async function findByText(options = {}) {
92
92
  if (engine === 'playwright') {
93
93
  // Playwright supports :has-text() natively
94
94
  const textSelector = exact ? `:text-is("${text}")` : `:has-text("${text}")`;
95
- return `${selector}${textSelector}`;
95
+ return await Promise.resolve(`${selector}${textSelector}`);
96
96
  } else {
97
97
  // For Puppeteer, we need to use XPath or evaluate
98
98
  // Return a special selector marker that will be handled by other methods
99
- return {
99
+ return await Promise.resolve({
100
100
  _isPuppeteerTextSelector: true,
101
101
  baseSelector: selector,
102
102
  text,
103
103
  exact,
104
- };
104
+ });
105
105
  }
106
106
  }
107
107
 
@@ -25,7 +25,6 @@ export async function waitForUrlCondition(options = {}) {
25
25
  const {
26
26
  getUrl,
27
27
  wait,
28
- evaluate,
29
28
  targetUrl,
30
29
  description,
31
30
  customCheck,
package/src/index.js CHANGED
@@ -11,7 +11,10 @@
11
11
  */
12
12
 
13
13
  // Export the factory function
14
- export { makeBrowserCommander } from './factory.js';
14
+ export {
15
+ makeBrowserCommander,
16
+ makeBrowserCommander as createCommander,
17
+ } from './factory.js';
15
18
 
16
19
  // Re-export all public APIs from exports module
17
20
  export * from './exports.js';
@@ -257,7 +257,7 @@ export async function clickElement(options = {}) {
257
257
  * @param {Function} options.log - Logger instance
258
258
  * @returns {Promise<{navigated: boolean, newUrl: string}>}
259
259
  */
260
- async function detectNavigation(options = {}) {
260
+ function detectNavigation(options = {}) {
261
261
  const { page, navigationManager, startUrl, log } = options;
262
262
 
263
263
  const currentUrl = page.url();