browser-commander 0.2.0 → 0.3.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 03e9ccb: Add isTimeoutError function for detecting timeout errors
8
+
9
+ Adds a new `isTimeoutError` function exported from the library that helps detect timeout errors from selector waiting operations. This function is complementary to `isNavigationError` and allows automation loops to handle timeout errors gracefully without crashing.
10
+
11
+ Usage example:
12
+
13
+ ```javascript
14
+ import { isTimeoutError } from 'browser-commander';
15
+
16
+ try {
17
+ await page.waitForSelector('.button');
18
+ } catch (error) {
19
+ if (isTimeoutError(error)) {
20
+ console.log('Timeout occurred, continuing with next item...');
21
+ }
22
+ }
23
+ ```
24
+
25
+ ## 0.2.1
26
+
27
+ ### Patch Changes
28
+
29
+ - Test patch release
30
+
3
31
  ## 0.2.0
4
32
 
5
33
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-commander",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Universal browser automation library that supports both Playwright and Puppeteer with a unified API",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -158,3 +158,32 @@ export function withNavigationSafety(fn, options = {}) {
158
158
  }
159
159
  };
160
160
  }
161
+
162
+ /**
163
+ * Check if an error is a timeout error from selector waiting
164
+ * These errors should be treated as non-fatal in automation loops
165
+ * @param {Error} error - The error to check
166
+ * @returns {boolean} - True if this is a timeout error
167
+ */
168
+ export function isTimeoutError(error) {
169
+ if (!error) {
170
+ return false;
171
+ }
172
+
173
+ // Check error name first (most reliable)
174
+ if (error.name === 'TimeoutError') {
175
+ return true;
176
+ }
177
+
178
+ // Check error message patterns (case-insensitive)
179
+ const message = (error.message || '').toLowerCase();
180
+ const timeoutErrorPatterns = [
181
+ 'waiting for selector',
182
+ 'timeout',
183
+ 'timeouterror',
184
+ 'timeout exceeded',
185
+ 'timed out',
186
+ ];
187
+
188
+ return timeoutErrorPatterns.some((pattern) => message.includes(pattern));
189
+ }
package/src/exports.js CHANGED
@@ -10,6 +10,7 @@ export { disableTranslateInPreferences } from './core/preferences.js';
10
10
  export { detectEngine } from './core/engine-detection.js';
11
11
  export {
12
12
  isNavigationError,
13
+ isTimeoutError,
13
14
  safeOperation,
14
15
  makeNavigationSafe,
15
16
  withNavigationSafety,
@@ -2,6 +2,7 @@ import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert';
3
3
  import {
4
4
  isNavigationError,
5
+ isTimeoutError,
5
6
  safeOperation,
6
7
  makeNavigationSafe,
7
8
  withNavigationSafety,
@@ -67,6 +68,80 @@ describe('navigation-safety', () => {
67
68
  });
68
69
  });
69
70
 
71
+ describe('isTimeoutError', () => {
72
+ it('should return false for null', () => {
73
+ assert.strictEqual(isTimeoutError(null), false);
74
+ });
75
+
76
+ it('should return false for undefined', () => {
77
+ assert.strictEqual(isTimeoutError(undefined), false);
78
+ });
79
+
80
+ it('should return false for error without message or name', () => {
81
+ assert.strictEqual(isTimeoutError({}), false);
82
+ });
83
+
84
+ it('should return false for regular error', () => {
85
+ const error = new Error('Some regular error');
86
+ assert.strictEqual(isTimeoutError(error), false);
87
+ });
88
+
89
+ it('should return true for error with name "TimeoutError"', () => {
90
+ const error = new Error('Something failed');
91
+ error.name = 'TimeoutError';
92
+ assert.strictEqual(isTimeoutError(error), true);
93
+ });
94
+
95
+ it('should return true for "waiting for selector" error', () => {
96
+ const error = new Error('Waiting for selector ".button" timed out');
97
+ assert.strictEqual(isTimeoutError(error), true);
98
+ });
99
+
100
+ it('should return true for "timeout" error (case-insensitive)', () => {
101
+ const error = new Error('Timeout while waiting for element');
102
+ assert.strictEqual(isTimeoutError(error), true);
103
+ });
104
+
105
+ it('should return true for "timeout exceeded" error', () => {
106
+ const error = new Error('timeout exceeded');
107
+ assert.strictEqual(isTimeoutError(error), true);
108
+ });
109
+
110
+ it('should return true for "timed out" error', () => {
111
+ const error = new Error('Operation timed out after 30000ms');
112
+ assert.strictEqual(isTimeoutError(error), true);
113
+ });
114
+
115
+ it('should return true for Playwright locator timeout error', () => {
116
+ const error = new Error(
117
+ 'locator.click: Timeout 30000ms exceeded. Waiting for selector "button.submit"'
118
+ );
119
+ assert.strictEqual(isTimeoutError(error), true);
120
+ });
121
+
122
+ it('should return true for Puppeteer waitForSelector timeout', () => {
123
+ const error = new Error(
124
+ 'waiting for selector `#myElement` failed: timeout 30000ms exceeded'
125
+ );
126
+ assert.strictEqual(isTimeoutError(error), true);
127
+ });
128
+
129
+ it('should handle uppercase TIMEOUT in message', () => {
130
+ const error = new Error('TIMEOUT ERROR occurred');
131
+ assert.strictEqual(isTimeoutError(error), true);
132
+ });
133
+
134
+ it('should not match navigation errors as timeout errors', () => {
135
+ const error = new Error('Execution context was destroyed');
136
+ assert.strictEqual(isTimeoutError(error), false);
137
+ });
138
+
139
+ it('should not match frame detached errors as timeout errors', () => {
140
+ const error = new Error('frame was detached');
141
+ assert.strictEqual(isTimeoutError(error), false);
142
+ });
143
+ });
144
+
70
145
  describe('safeOperation', () => {
71
146
  it('should return success result on successful operation', async () => {
72
147
  const result = await safeOperation(async () => 'success');