browser-commander 0.2.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.
Files changed (82) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.github/workflows/release.yml +296 -0
  4. package/.husky/pre-commit +1 -0
  5. package/.jscpd.json +20 -0
  6. package/.prettierignore +7 -0
  7. package/.prettierrc +10 -0
  8. package/CHANGELOG.md +32 -0
  9. package/LICENSE +24 -0
  10. package/README.md +320 -0
  11. package/bunfig.toml +3 -0
  12. package/deno.json +7 -0
  13. package/eslint.config.js +125 -0
  14. package/examples/react-test-app/index.html +25 -0
  15. package/examples/react-test-app/package.json +19 -0
  16. package/examples/react-test-app/src/App.jsx +473 -0
  17. package/examples/react-test-app/src/main.jsx +10 -0
  18. package/examples/react-test-app/src/styles.css +323 -0
  19. package/examples/react-test-app/vite.config.js +9 -0
  20. package/package.json +89 -0
  21. package/scripts/changeset-version.mjs +38 -0
  22. package/scripts/create-github-release.mjs +93 -0
  23. package/scripts/create-manual-changeset.mjs +86 -0
  24. package/scripts/format-github-release.mjs +83 -0
  25. package/scripts/format-release-notes.mjs +216 -0
  26. package/scripts/instant-version-bump.mjs +121 -0
  27. package/scripts/merge-changesets.mjs +260 -0
  28. package/scripts/publish-to-npm.mjs +126 -0
  29. package/scripts/setup-npm.mjs +37 -0
  30. package/scripts/validate-changeset.mjs +262 -0
  31. package/scripts/version-and-commit.mjs +237 -0
  32. package/src/ARCHITECTURE.md +270 -0
  33. package/src/README.md +517 -0
  34. package/src/bindings.js +298 -0
  35. package/src/browser/launcher.js +93 -0
  36. package/src/browser/navigation.js +513 -0
  37. package/src/core/constants.js +24 -0
  38. package/src/core/engine-adapter.js +466 -0
  39. package/src/core/engine-detection.js +49 -0
  40. package/src/core/logger.js +21 -0
  41. package/src/core/navigation-manager.js +503 -0
  42. package/src/core/navigation-safety.js +160 -0
  43. package/src/core/network-tracker.js +373 -0
  44. package/src/core/page-session.js +299 -0
  45. package/src/core/page-trigger-manager.js +564 -0
  46. package/src/core/preferences.js +46 -0
  47. package/src/elements/content.js +197 -0
  48. package/src/elements/locators.js +243 -0
  49. package/src/elements/selectors.js +360 -0
  50. package/src/elements/visibility.js +166 -0
  51. package/src/exports.js +121 -0
  52. package/src/factory.js +192 -0
  53. package/src/high-level/universal-logic.js +206 -0
  54. package/src/index.js +17 -0
  55. package/src/interactions/click.js +684 -0
  56. package/src/interactions/fill.js +383 -0
  57. package/src/interactions/scroll.js +341 -0
  58. package/src/utilities/url.js +33 -0
  59. package/src/utilities/wait.js +135 -0
  60. package/tests/e2e/playwright.e2e.test.js +442 -0
  61. package/tests/e2e/puppeteer.e2e.test.js +408 -0
  62. package/tests/helpers/mocks.js +542 -0
  63. package/tests/unit/bindings.test.js +218 -0
  64. package/tests/unit/browser/navigation.test.js +345 -0
  65. package/tests/unit/core/constants.test.js +72 -0
  66. package/tests/unit/core/engine-adapter.test.js +170 -0
  67. package/tests/unit/core/engine-detection.test.js +81 -0
  68. package/tests/unit/core/logger.test.js +80 -0
  69. package/tests/unit/core/navigation-safety.test.js +202 -0
  70. package/tests/unit/core/network-tracker.test.js +198 -0
  71. package/tests/unit/core/page-trigger-manager.test.js +358 -0
  72. package/tests/unit/elements/content.test.js +318 -0
  73. package/tests/unit/elements/locators.test.js +236 -0
  74. package/tests/unit/elements/selectors.test.js +302 -0
  75. package/tests/unit/elements/visibility.test.js +234 -0
  76. package/tests/unit/factory.test.js +174 -0
  77. package/tests/unit/high-level/universal-logic.test.js +299 -0
  78. package/tests/unit/interactions/click.test.js +340 -0
  79. package/tests/unit/interactions/fill.test.js +378 -0
  80. package/tests/unit/interactions/scroll.test.js +330 -0
  81. package/tests/unit/utilities/url.test.js +63 -0
  82. package/tests/unit/utilities/wait.test.js +207 -0
@@ -0,0 +1,513 @@
1
+ /**
2
+ * Navigation-related browser operations
3
+ *
4
+ * This module provides navigation functions that can work with or without
5
+ * the NavigationManager for backwards compatibility.
6
+ */
7
+
8
+ import { TIMING } from '../core/constants.js';
9
+ import { isNavigationError } from '../core/navigation-safety.js';
10
+ import { isActionStoppedError } from '../core/page-trigger-manager.js';
11
+
12
+ /**
13
+ * Default verification function for navigation operations.
14
+ * Verifies that navigation completed by checking:
15
+ * - URL matches expected pattern (if provided)
16
+ * - Page is in a ready state
17
+ *
18
+ * @param {Object} options - Verification options
19
+ * @param {Object} options.page - Browser page object
20
+ * @param {string} options.expectedUrl - Expected URL or URL pattern (optional)
21
+ * @param {string} options.startUrl - URL before navigation
22
+ * @returns {Promise<{verified: boolean, actualUrl: string, reason: string}>}
23
+ */
24
+ export async function defaultNavigationVerification(options = {}) {
25
+ const { page, expectedUrl, startUrl } = options;
26
+
27
+ try {
28
+ const actualUrl = page.url();
29
+
30
+ // If expected URL is provided, verify it matches
31
+ if (expectedUrl) {
32
+ // Check for exact match or pattern match
33
+ if (actualUrl === expectedUrl) {
34
+ return { verified: true, actualUrl, reason: 'exact URL match' };
35
+ }
36
+ // Check if expected URL is contained in actual URL (for patterns)
37
+ if (
38
+ actualUrl.includes(expectedUrl) ||
39
+ actualUrl.startsWith(expectedUrl)
40
+ ) {
41
+ return { verified: true, actualUrl, reason: 'URL pattern match' };
42
+ }
43
+ // Check if it's a regex pattern
44
+ if (expectedUrl instanceof RegExp && expectedUrl.test(actualUrl)) {
45
+ return { verified: true, actualUrl, reason: 'URL regex match' };
46
+ }
47
+
48
+ return {
49
+ verified: false,
50
+ actualUrl,
51
+ reason: `URL mismatch: expected "${expectedUrl}", got "${actualUrl}"`,
52
+ };
53
+ }
54
+
55
+ // No expected URL - just verify URL changed from start
56
+ if (startUrl && actualUrl !== startUrl) {
57
+ return { verified: true, actualUrl, reason: 'URL changed from start' };
58
+ }
59
+
60
+ // If no start URL and no expected URL, assume success
61
+ return { verified: true, actualUrl, reason: 'navigation completed' };
62
+ } catch (error) {
63
+ if (isNavigationError(error) || isActionStoppedError(error)) {
64
+ return {
65
+ verified: false,
66
+ actualUrl: '',
67
+ reason: 'error during verification',
68
+ navigationError: true,
69
+ };
70
+ }
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Verify navigation operation with retry logic
77
+ * @param {Object} options - Verification options
78
+ * @param {Object} options.page - Browser page object
79
+ * @param {string} options.expectedUrl - Expected URL (optional)
80
+ * @param {string} options.startUrl - URL before navigation
81
+ * @param {Function} options.verifyFn - Custom verification function (optional)
82
+ * @param {number} options.timeout - Verification timeout in ms (default: TIMING.VERIFICATION_TIMEOUT)
83
+ * @param {number} options.retryInterval - Interval between retries (default: TIMING.VERIFICATION_RETRY_INTERVAL)
84
+ * @param {Function} options.log - Logger instance
85
+ * @returns {Promise<{verified: boolean, actualUrl: string, reason: string, attempts: number}>}
86
+ */
87
+ export async function verifyNavigation(options = {}) {
88
+ const {
89
+ page,
90
+ expectedUrl,
91
+ startUrl,
92
+ verifyFn = defaultNavigationVerification,
93
+ timeout = TIMING.VERIFICATION_TIMEOUT,
94
+ retryInterval = TIMING.VERIFICATION_RETRY_INTERVAL,
95
+ log = { debug: () => {} },
96
+ } = options;
97
+
98
+ const startTime = Date.now();
99
+ let attempts = 0;
100
+ let lastResult = { verified: false, actualUrl: '', reason: '' };
101
+
102
+ while (Date.now() - startTime < timeout) {
103
+ attempts++;
104
+ lastResult = await verifyFn({
105
+ page,
106
+ expectedUrl,
107
+ startUrl,
108
+ });
109
+
110
+ if (lastResult.verified) {
111
+ log.debug(
112
+ () =>
113
+ `✅ Navigation verification succeeded after ${attempts} attempt(s): ${lastResult.reason}`
114
+ );
115
+ return { ...lastResult, attempts };
116
+ }
117
+
118
+ if (lastResult.navigationError) {
119
+ log.debug(() => '⚠️ Navigation/stop detected during verification');
120
+ return { ...lastResult, attempts };
121
+ }
122
+
123
+ // Wait before next retry
124
+ await new Promise((resolve) => setTimeout(resolve, retryInterval));
125
+ }
126
+
127
+ log.debug(
128
+ () =>
129
+ `❌ Navigation verification failed after ${attempts} attempts: ${lastResult.reason}`
130
+ );
131
+ return { ...lastResult, attempts };
132
+ }
133
+
134
+ /**
135
+ * Wait for URL to stabilize (no redirects happening)
136
+ * This is a legacy polling-based approach for backwards compatibility.
137
+ * When navigationManager is available, use waitForPageReady instead.
138
+ *
139
+ * @param {Object} options - Configuration options
140
+ * @param {Object} options.page - Browser page object
141
+ * @param {Function} options.log - Logger instance
142
+ * @param {Function} options.wait - Wait function
143
+ * @param {Object} options.navigationManager - NavigationManager instance (optional)
144
+ * @param {number} options.stableChecks - Number of consecutive stable checks required (default: 3)
145
+ * @param {number} options.checkInterval - Interval between stability checks in ms (default: 1000)
146
+ * @param {number} options.timeout - Maximum time to wait for stabilization in ms (default: 30000)
147
+ * @param {string} options.reason - Reason for stabilization (for logging)
148
+ * @returns {Promise<boolean>} - True if stabilized, false if timeout
149
+ */
150
+ export async function waitForUrlStabilization(options = {}) {
151
+ const {
152
+ page,
153
+ log,
154
+ wait,
155
+ navigationManager,
156
+ stableChecks = 3,
157
+ checkInterval = 1000,
158
+ timeout = 30000,
159
+ reason = 'URL stabilization',
160
+ } = options;
161
+
162
+ // If NavigationManager is available, delegate to it
163
+ if (navigationManager) {
164
+ return navigationManager.waitForPageReady({ timeout, reason });
165
+ }
166
+
167
+ // Legacy polling-based approach
168
+ log.debug(() => `⏳ Waiting for URL to stabilize (${reason})...`);
169
+ let stableCount = 0;
170
+ let lastUrl = page.url();
171
+ const startTime = Date.now();
172
+
173
+ while (stableCount < stableChecks) {
174
+ // Check timeout
175
+ if (Date.now() - startTime > timeout) {
176
+ log.debug(
177
+ () => `⚠️ URL stabilization timeout after ${timeout}ms (${reason})`
178
+ );
179
+ return false;
180
+ }
181
+
182
+ await wait({ ms: checkInterval, reason: 'checking URL stability' });
183
+ const currentUrl = page.url();
184
+
185
+ if (currentUrl === lastUrl) {
186
+ stableCount++;
187
+ log.debug(
188
+ () =>
189
+ `🔍 [VERBOSE] URL stable for ${stableCount}/${stableChecks} checks: ${currentUrl}`
190
+ );
191
+ } else {
192
+ stableCount = 0;
193
+ lastUrl = currentUrl;
194
+ log.debug(
195
+ () =>
196
+ `🔍 [VERBOSE] URL changed to: ${currentUrl}, resetting stability counter`
197
+ );
198
+ }
199
+ }
200
+
201
+ log.debug(() => `✅ URL stabilized (${reason})`);
202
+ return true;
203
+ }
204
+
205
+ /**
206
+ * Navigate to URL with full wait for page ready
207
+ * @param {Object} options - Configuration options
208
+ * @param {Object} options.page - Browser page object
209
+ * @param {Function} options.waitForUrlStabilization - URL stabilization function (legacy)
210
+ * @param {Object} options.navigationManager - NavigationManager instance (preferred)
211
+ * @param {Function} options.log - Logger instance (optional)
212
+ * @param {string} options.url - URL to navigate to
213
+ * @param {string} options.waitUntil - Wait until condition (default: 'domcontentloaded')
214
+ * @param {boolean} options.waitForStableUrlBefore - Wait for URL to stabilize BEFORE navigation (default: true)
215
+ * @param {boolean} options.waitForStableUrlAfter - Wait for URL to stabilize AFTER navigation (default: true)
216
+ * @param {boolean} options.waitForNetworkIdle - Wait for all network requests to complete (default: true)
217
+ * @param {number} options.stableChecks - Number of consecutive stable checks required (default: 3)
218
+ * @param {number} options.checkInterval - Interval between stability checks in ms (default: 1000)
219
+ * @param {number} options.timeout - Navigation timeout in ms (default: 240000)
220
+ * @param {boolean} options.verify - Whether to verify the navigation (default: true)
221
+ * @param {Function} options.verifyFn - Custom verification function (optional)
222
+ * @param {number} options.verificationTimeout - Verification timeout in ms (default: TIMING.VERIFICATION_TIMEOUT)
223
+ * @returns {Promise<{navigated: boolean, verified: boolean, actualUrl?: string, reason?: string}>}
224
+ */
225
+ export async function goto(options = {}) {
226
+ const {
227
+ page,
228
+ waitForUrlStabilization: stabilizeFn,
229
+ navigationManager,
230
+ log = { debug: () => {} },
231
+ url,
232
+ waitUntil = 'domcontentloaded',
233
+ waitForStableUrlBefore = true,
234
+ waitForStableUrlAfter = true,
235
+ waitForNetworkIdle = true,
236
+ stableChecks = 3,
237
+ checkInterval = 1000,
238
+ timeout = 240000,
239
+ verify = true,
240
+ verifyFn,
241
+ verificationTimeout = TIMING.VERIFICATION_TIMEOUT,
242
+ } = options;
243
+
244
+ if (!url) {
245
+ throw new Error('url is required in options');
246
+ }
247
+
248
+ const startUrl = page.url();
249
+
250
+ // If NavigationManager is available, use it for full navigation handling
251
+ if (navigationManager) {
252
+ try {
253
+ const navigated = await navigationManager.navigate({
254
+ url,
255
+ waitUntil,
256
+ timeout,
257
+ });
258
+
259
+ // Verify navigation if requested
260
+ if (verify && navigated) {
261
+ const verificationResult = await verifyNavigation({
262
+ page,
263
+ expectedUrl: url,
264
+ startUrl,
265
+ verifyFn,
266
+ timeout: verificationTimeout,
267
+ log,
268
+ });
269
+
270
+ return {
271
+ navigated: true,
272
+ verified: verificationResult.verified,
273
+ actualUrl: verificationResult.actualUrl,
274
+ reason: verificationResult.reason,
275
+ };
276
+ }
277
+
278
+ return { navigated, verified: navigated, actualUrl: page.url() };
279
+ } catch (error) {
280
+ if (isNavigationError(error) || isActionStoppedError(error)) {
281
+ // Navigation was stopped by page trigger or navigation error
282
+ // This is not a failure - it means another action took over
283
+ return {
284
+ navigated: false,
285
+ verified: false,
286
+ reason: 'navigation stopped/interrupted',
287
+ };
288
+ }
289
+ throw error;
290
+ }
291
+ }
292
+
293
+ // Legacy approach without NavigationManager
294
+ try {
295
+ // Wait for URL to stabilize BEFORE navigation (to avoid interrupting natural redirects)
296
+ if (waitForStableUrlBefore && stabilizeFn) {
297
+ await stabilizeFn({
298
+ stableChecks,
299
+ checkInterval,
300
+ reason: 'before navigation',
301
+ });
302
+ }
303
+
304
+ // Navigate to the URL
305
+ await page.goto(url, { waitUntil, timeout });
306
+
307
+ // Wait for URL to stabilize AFTER navigation (to ensure all redirects are complete)
308
+ if (waitForStableUrlAfter && stabilizeFn) {
309
+ await stabilizeFn({
310
+ stableChecks,
311
+ checkInterval,
312
+ reason: 'after navigation',
313
+ });
314
+ }
315
+
316
+ // Verify navigation if requested
317
+ if (verify) {
318
+ const verificationResult = await verifyNavigation({
319
+ page,
320
+ expectedUrl: url,
321
+ startUrl,
322
+ verifyFn,
323
+ timeout: verificationTimeout,
324
+ log,
325
+ });
326
+
327
+ return {
328
+ navigated: true,
329
+ verified: verificationResult.verified,
330
+ actualUrl: verificationResult.actualUrl,
331
+ reason: verificationResult.reason,
332
+ };
333
+ }
334
+
335
+ return { navigated: true, verified: true, actualUrl: page.url() };
336
+ } catch (error) {
337
+ if (isNavigationError(error) || isActionStoppedError(error)) {
338
+ console.log(
339
+ '⚠️ Navigation was interrupted/stopped, recovering gracefully'
340
+ );
341
+ return {
342
+ navigated: false,
343
+ verified: false,
344
+ reason: 'navigation interrupted/stopped',
345
+ };
346
+ }
347
+ throw error;
348
+ }
349
+ }
350
+
351
+ /**
352
+ * Wait for navigation
353
+ * @param {Object} options - Configuration options
354
+ * @param {Object} options.page - Browser page object
355
+ * @param {Object} options.navigationManager - NavigationManager instance (optional)
356
+ * @param {number} options.timeout - Timeout in ms
357
+ * @returns {Promise<boolean>} - True if navigation completed, false on error
358
+ */
359
+ export async function waitForNavigation(options = {}) {
360
+ const { page, navigationManager, timeout } = options;
361
+
362
+ // If NavigationManager is available, use it
363
+ if (navigationManager) {
364
+ return navigationManager.waitForNavigation({ timeout });
365
+ }
366
+
367
+ // Legacy approach
368
+ try {
369
+ await page.waitForNavigation(timeout ? { timeout } : undefined);
370
+ return true;
371
+ } catch (error) {
372
+ if (isNavigationError(error)) {
373
+ console.log(
374
+ '⚠️ waitForNavigation was interrupted, continuing gracefully'
375
+ );
376
+ return false;
377
+ }
378
+ throw error;
379
+ }
380
+ }
381
+
382
+ /**
383
+ * Wait for page to be fully ready (DOM loaded + network idle + no redirects)
384
+ * This is the recommended method for ensuring page is ready for manipulation.
385
+ *
386
+ * @param {Object} options - Configuration options
387
+ * @param {Object} options.page - Browser page object
388
+ * @param {Object} options.navigationManager - NavigationManager instance (required for full functionality)
389
+ * @param {Object} options.networkTracker - NetworkTracker instance (optional)
390
+ * @param {Function} options.log - Logger instance
391
+ * @param {Function} options.wait - Wait function
392
+ * @param {number} options.timeout - Maximum time to wait (default: 30000ms)
393
+ * @param {string} options.reason - Reason for waiting (for logging)
394
+ * @returns {Promise<boolean>} - True if ready, false if timeout
395
+ */
396
+ export async function waitForPageReady(options = {}) {
397
+ const {
398
+ page,
399
+ navigationManager,
400
+ networkTracker,
401
+ log,
402
+ wait,
403
+ timeout = 30000,
404
+ reason = 'page ready',
405
+ } = options;
406
+
407
+ // If NavigationManager is available, delegate to it
408
+ if (navigationManager) {
409
+ return navigationManager.waitForPageReady({ timeout, reason });
410
+ }
411
+
412
+ // Fallback: use network tracker directly if available
413
+ if (networkTracker) {
414
+ log.debug(() => `⏳ Waiting for page ready (${reason})...`);
415
+ const startTime = Date.now();
416
+
417
+ // Wait for network idle
418
+ const networkIdle = await networkTracker.waitForNetworkIdle({
419
+ timeout,
420
+ });
421
+
422
+ const elapsed = Date.now() - startTime;
423
+ if (networkIdle) {
424
+ log.debug(() => `✅ Page ready after ${elapsed}ms (${reason})`);
425
+ } else {
426
+ log.debug(() => `⚠️ Page ready timeout after ${elapsed}ms (${reason})`);
427
+ }
428
+
429
+ return networkIdle;
430
+ }
431
+
432
+ // Minimal fallback: just wait a bit for DOM to settle
433
+ log.debug(() => `⏳ Waiting for page ready - minimal mode (${reason})...`);
434
+ await wait({ ms: 1000, reason: 'page settle time' });
435
+ return true;
436
+ }
437
+
438
+ /**
439
+ * Wait for any ongoing navigation and network requests to complete.
440
+ * Use this after actions that might trigger navigation (like clicks).
441
+ *
442
+ * @param {Object} options - Configuration options
443
+ * @param {Object} options.page - Browser page object
444
+ * @param {Object} options.navigationManager - NavigationManager instance
445
+ * @param {Object} options.networkTracker - NetworkTracker instance
446
+ * @param {Function} options.log - Logger instance
447
+ * @param {Function} options.wait - Wait function
448
+ * @param {number} options.navigationCheckDelay - Time to wait for potential navigation to start (default: 500ms)
449
+ * @param {number} options.timeout - Maximum time to wait (default: 30000ms)
450
+ * @param {string} options.reason - Reason for waiting (for logging)
451
+ * @returns {Promise<{navigated: boolean, ready: boolean}>}
452
+ */
453
+ export async function waitAfterAction(options = {}) {
454
+ const {
455
+ page,
456
+ navigationManager,
457
+ networkTracker,
458
+ log,
459
+ wait,
460
+ navigationCheckDelay = 500,
461
+ timeout = 30000,
462
+ reason = 'after action',
463
+ } = options;
464
+
465
+ const startUrl = page.url();
466
+ const startTime = Date.now();
467
+
468
+ log.debug(() => `⏳ Waiting after action (${reason})...`);
469
+
470
+ // Wait briefly for potential navigation to start
471
+ await wait({ ms: navigationCheckDelay, reason: 'checking for navigation' });
472
+
473
+ // Check if navigation is in progress or URL changed
474
+ const currentUrl = page.url();
475
+ const urlChanged = currentUrl !== startUrl;
476
+
477
+ if (navigationManager && navigationManager.isNavigating()) {
478
+ log.debug(() => '🔄 Navigation in progress, waiting for completion...');
479
+ await navigationManager.waitForNavigation({
480
+ timeout: timeout - (Date.now() - startTime),
481
+ });
482
+ return { navigated: true, ready: true };
483
+ }
484
+
485
+ if (urlChanged) {
486
+ log.debug(() => `🔄 URL changed: ${startUrl} → ${currentUrl}`);
487
+
488
+ // Wait for page to be fully ready
489
+ await waitForPageReady({
490
+ page,
491
+ navigationManager,
492
+ networkTracker,
493
+ log,
494
+ wait,
495
+ timeout: timeout - (Date.now() - startTime),
496
+ reason: 'after URL change',
497
+ });
498
+
499
+ return { navigated: true, ready: true };
500
+ }
501
+
502
+ // No navigation detected, just wait for network idle
503
+ // Use shorter idle time since this is just for XHR completion, not full page load
504
+ if (networkTracker) {
505
+ const idle = await networkTracker.waitForNetworkIdle({
506
+ timeout: Math.max(0, timeout - (Date.now() - startTime)),
507
+ idleTime: 2000, // Shorter idle time for non-navigation actions
508
+ });
509
+ return { navigated: false, ready: idle };
510
+ }
511
+
512
+ return { navigated: false, ready: true };
513
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Common Chrome arguments used across both Playwright and Puppeteer
3
+ */
4
+ export const CHROME_ARGS = [
5
+ '--disable-session-crashed-bubble',
6
+ '--hide-crash-restore-bubble',
7
+ '--disable-infobars',
8
+ '--no-first-run',
9
+ '--no-default-browser-check',
10
+ '--disable-crash-restore',
11
+ ];
12
+
13
+ /**
14
+ * Timing constants for browser operations
15
+ */
16
+ export const TIMING = {
17
+ SCROLL_ANIMATION_WAIT: 300, // Wait time for scroll animations to complete
18
+ DEFAULT_WAIT_AFTER_SCROLL: 1000, // Default wait after scrolling to element
19
+ VISIBILITY_CHECK_TIMEOUT: 100, // Timeout for quick visibility checks
20
+ DEFAULT_TIMEOUT: 5000, // Default timeout for most operations
21
+ NAVIGATION_TIMEOUT: 30000, // Default timeout for navigation operations
22
+ VERIFICATION_TIMEOUT: 3000, // Default timeout for action verification
23
+ VERIFICATION_RETRY_INTERVAL: 100, // Interval between verification retries
24
+ };