@checkly/playwright-core 1.41.23 → 1.41.26

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 (81) hide show
  1. package/lib/cli/driver.js +1 -9
  2. package/lib/cli/program.js +3 -11
  3. package/lib/client/channelOwner.js +1 -1
  4. package/lib/client/clientHelper.js +1 -5
  5. package/lib/client/connection.js +1 -1
  6. package/lib/client/consoleMessage.js +1 -1
  7. package/lib/client/electron.js +0 -3
  8. package/lib/client/events.js +0 -1
  9. package/lib/client/frame.js +1 -2
  10. package/lib/client/harRouter.js +1 -7
  11. package/lib/client/page.js +6 -25
  12. package/lib/common/debugLogger.js +0 -1
  13. package/lib/common/socksProxy.js +1 -1
  14. package/lib/generated/consoleApiSource.js +1 -1
  15. package/lib/generated/injectedScriptSource.js +1 -1
  16. package/lib/generated/recorderSource.js +1 -1
  17. package/lib/outofprocess.js +1 -1
  18. package/lib/protocol/validator.js +29 -46
  19. package/lib/remote/playwrightConnection.js +1 -1
  20. package/lib/remote/playwrightServer.js +166 -72
  21. package/lib/server/android/android.js +1 -1
  22. package/lib/server/browserType.js +2 -2
  23. package/lib/server/chromium/chromium.js +4 -5
  24. package/lib/server/chromium/crConnection.js +1 -1
  25. package/lib/server/chromium/crPage.js +2 -45
  26. package/lib/server/console.js +3 -1
  27. package/lib/server/debugController.js +3 -0
  28. package/lib/server/deviceDescriptorsSource.json +50 -50
  29. package/lib/server/dispatchers/browserContextDispatcher.js +2 -3
  30. package/lib/server/dispatchers/dispatcher.js +10 -9
  31. package/lib/server/dispatchers/electronDispatcher.js +0 -13
  32. package/lib/server/dispatchers/frameDispatcher.js +6 -0
  33. package/lib/server/dispatchers/pageDispatcher.js +10 -14
  34. package/lib/server/dom.js +167 -130
  35. package/lib/server/electron/electron.js +12 -38
  36. package/lib/server/electron/loader.js +2 -4
  37. package/lib/server/firefox/ffAccessibility.js +1 -2
  38. package/lib/server/firefox/ffConnection.js +1 -1
  39. package/lib/server/firefox/ffPage.js +1 -1
  40. package/lib/server/frames.js +20 -39
  41. package/lib/server/helper.js +1 -1
  42. package/lib/server/page.js +5 -49
  43. package/lib/server/pipeTransport.js +1 -1
  44. package/lib/server/playwright.js +1 -1
  45. package/lib/server/progress.js +11 -3
  46. package/lib/server/recorder/csharp.js +1 -1
  47. package/lib/server/recorder.js +1 -1
  48. package/lib/server/registry/browserFetcher.js +1 -1
  49. package/lib/server/registry/dependencies.js +4 -5
  50. package/lib/server/registry/index.js +30 -48
  51. package/lib/server/registry/nativeDeps.js +94 -0
  52. package/lib/server/trace/recorder/snapshotter.js +1 -1
  53. package/lib/server/trace/recorder/tracing.js +2 -5
  54. package/lib/server/trace/viewer/traceViewer.js +1 -1
  55. package/lib/server/transport.js +2 -4
  56. package/lib/server/webkit/wkConnection.js +1 -1
  57. package/lib/server/webkit/wkPage.js +2 -2
  58. package/lib/utils/comparators.js +4 -4
  59. package/lib/utils/fileUtils.js +0 -4
  60. package/lib/utils/hostPlatform.js +1 -1
  61. package/lib/utils/index.js +0 -11
  62. package/lib/utils/isomorphic/locatorParser.js +4 -6
  63. package/lib/utils/network.js +0 -33
  64. package/lib/utils/processLauncher.js +0 -7
  65. package/lib/vite/htmlReport/index.html +11 -11
  66. package/lib/vite/traceViewer/assets/codeMirrorModule-2ImvVqMb.js +24 -0
  67. package/lib/vite/traceViewer/assets/codeMirrorModule-2mdjgmqe.js +24 -0
  68. package/lib/vite/traceViewer/assets/codeMirrorModule-GJA8DRmd.js +24 -0
  69. package/lib/vite/traceViewer/assets/wsPort-93o0i57c.js +69 -0
  70. package/lib/vite/traceViewer/assets/wsPort-qI0zJPR7.js +69 -0
  71. package/lib/vite/traceViewer/assets/wsPort-sh0wpjYp.js +69 -0
  72. package/lib/vite/traceViewer/index.LR1HufLs.js +2 -0
  73. package/lib/vite/traceViewer/index.Ox-CymYJ.js +2 -0
  74. package/lib/vite/traceViewer/index.html +3 -3
  75. package/lib/vite/traceViewer/index.krETyIB_.js +2 -0
  76. package/lib/vite/traceViewer/sw.bundle.js +4 -4
  77. package/lib/vite/traceViewer/uiMode.Rcwfn0db.js +10 -0
  78. package/lib/vite/traceViewer/uiMode.YGPXSUMv.js +10 -0
  79. package/lib/vite/traceViewer/uiMode.YYFJGvtV.js +10 -0
  80. package/lib/vite/traceViewer/uiMode.html +3 -3
  81. package/package.json +1 -1
package/lib/server/dom.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.NonRecoverableDOMError = exports.FrameExecutionContext = exports.ElementHandle = void 0;
6
+ exports.NonRecoverableDOMError = exports.InjectedScriptPollHandler = exports.FrameExecutionContext = exports.ElementHandle = void 0;
7
7
  exports.assertDone = assertDone;
8
8
  exports.isNonRecoverableDOMError = isNonRecoverableDOMError;
9
9
  exports.kUnableToAdoptErrorMessage = void 0;
@@ -136,6 +136,17 @@ class ElementHandle extends js.JSHandle {
136
136
  return 'error:notconnected';
137
137
  }
138
138
  }
139
+ async evaluatePoll(progress, pageFunction, arg) {
140
+ try {
141
+ const utility = await this._frame._utilityContext();
142
+ const poll = await utility.evaluateHandle(pageFunction, [await utility.injectedScript(), this, arg]);
143
+ const pollHandler = new InjectedScriptPollHandler(progress, poll);
144
+ return await pollHandler.finish();
145
+ } catch (e) {
146
+ if (js.isJavaScriptErrorInEvaluate(e) || (0, _protocolError.isSessionClosedError)(e)) throw e;
147
+ return 'error:notconnected';
148
+ }
149
+ }
139
150
  async ownerFrame() {
140
151
  const frameId = await this._page._delegate.getOwnerFrame(this);
141
152
  if (!frameId) return null;
@@ -177,19 +188,25 @@ class ElementHandle extends js.JSHandle {
177
188
  return await this._page._delegate.scrollRectIntoViewIfNeeded(this, rect);
178
189
  }
179
190
  async _waitAndScrollIntoViewIfNeeded(progress, waitForVisible) {
180
- const result = await this._retryAction(progress, 'scroll into view', async () => {
181
- progress.log(` waiting for element to be stable`);
182
- const waitResult = await this.evaluateInUtility(async ([injected, node, {
183
- waitForVisible
184
- }]) => {
185
- return await injected.checkElementStates(node, waitForVisible ? ['visible', 'stable'] : ['stable']);
186
- }, {
187
- waitForVisible
188
- });
189
- if (waitResult) return waitResult;
190
- return await this._scrollRectIntoViewIfNeeded();
191
- }, {});
192
- assertDone(throwRetargetableDOMError(result));
191
+ const timeouts = [0, 50, 100, 250];
192
+ while (progress.isRunning()) {
193
+ assertDone(throwRetargetableDOMError(await this._waitForElementStates(progress, waitForVisible ? ['visible', 'stable'] : ['stable'], false /* force */)));
194
+ progress.throwIfAborted(); // Avoid action that has side-effects.
195
+ const result = throwRetargetableDOMError(await this._scrollRectIntoViewIfNeeded());
196
+ if (result === 'error:notvisible') {
197
+ if (!waitForVisible) {
198
+ var _timeouts$shift;
199
+ // Wait for a timeout to avoid retrying too often when not waiting for visible.
200
+ // If we wait for visible, this should be covered by _waitForElementStates instead.
201
+ const timeout = (_timeouts$shift = timeouts.shift()) !== null && _timeouts$shift !== void 0 ? _timeouts$shift : 500;
202
+ progress.log(` element is not displayed, retrying in ${timeout}ms`);
203
+ await new Promise(f => setTimeout(f, timeout));
204
+ }
205
+ continue;
206
+ }
207
+ assertDone(result);
208
+ return;
209
+ }
193
210
  }
194
211
  async scrollIntoViewIfNeeded(metadata, options = {}) {
195
212
  const controller = new _progress.ProgressController(metadata, this);
@@ -244,10 +261,25 @@ class ElementHandle extends js.JSHandle {
244
261
  y: box.y + border.top + offset.y
245
262
  };
246
263
  }
247
- async _retryAction(progress, actionName, action, options) {
264
+ async _retryPointerAction(progress, actionName, waitForEnabled, action, options) {
248
265
  let retry = 0;
249
266
  // We progressively wait longer between retries, up to 500ms.
250
267
  const waitTime = [0, 20, 100, 100, 500];
268
+
269
+ // By default, we scroll with protocol method to reveal the action point.
270
+ // However, that might not work to scroll from under position:sticky elements
271
+ // that overlay the target element. To fight this, we cycle through different
272
+ // scroll alignments. This works in most scenarios.
273
+ const scrollOptions = [undefined, {
274
+ block: 'end',
275
+ inline: 'end'
276
+ }, {
277
+ block: 'center',
278
+ inline: 'center'
279
+ }, {
280
+ block: 'start',
281
+ inline: 'start'
282
+ }];
251
283
  while (progress.isRunning()) {
252
284
  if (retry) {
253
285
  progress.log(`retrying ${actionName} action${options.trial ? ' (trial run)' : ''}, attempt #${retry}`);
@@ -260,8 +292,8 @@ class ElementHandle extends js.JSHandle {
260
292
  } else {
261
293
  progress.log(`attempting ${actionName} action${options.trial ? ' (trial run)' : ''}`);
262
294
  }
263
- if (!options.skipLocatorHandlersCheckpoint && !options.force) await this._frame._page.performLocatorHandlersCheckpoint(progress);
264
- const result = await action(retry);
295
+ const forceScrollOptions = scrollOptions[retry % scrollOptions.length];
296
+ const result = await this._performPointerAction(progress, actionName, waitForEnabled, action, forceScrollOptions, options);
265
297
  ++retry;
266
298
  if (result === 'error:notvisible') {
267
299
  if (options.force) throw new NonRecoverableDOMError('Element is not visible');
@@ -273,47 +305,14 @@ class ElementHandle extends js.JSHandle {
273
305
  progress.log(' element is outside of the viewport');
274
306
  continue;
275
307
  }
276
- if (result === 'error:optionsnotfound') {
277
- progress.log(' did not find some options');
278
- continue;
279
- }
280
308
  if (typeof result === 'object' && 'hitTargetDescription' in result) {
281
309
  progress.log(` ${result.hitTargetDescription} intercepts pointer events`);
282
310
  continue;
283
311
  }
284
- if (typeof result === 'object' && 'missingState' in result) {
285
- progress.log(` element is not ${result.missingState}`);
286
- continue;
287
- }
288
312
  return result;
289
313
  }
290
314
  return 'done';
291
315
  }
292
- async _retryPointerAction(progress, actionName, waitForEnabled, action, options) {
293
- // Note: do not perform locator handlers checkpoint to avoid moving the mouse in the middle of a drag operation.
294
- const skipLocatorHandlersCheckpoint = actionName === 'move and up';
295
- return await this._retryAction(progress, actionName, async retry => {
296
- // By default, we scroll with protocol method to reveal the action point.
297
- // However, that might not work to scroll from under position:sticky elements
298
- // that overlay the target element. To fight this, we cycle through different
299
- // scroll alignments. This works in most scenarios.
300
- const scrollOptions = [undefined, {
301
- block: 'end',
302
- inline: 'end'
303
- }, {
304
- block: 'center',
305
- inline: 'center'
306
- }, {
307
- block: 'start',
308
- inline: 'start'
309
- }];
310
- const forceScrollOptions = scrollOptions[retry % scrollOptions.length];
311
- return await this._performPointerAction(progress, actionName, waitForEnabled, action, forceScrollOptions, options);
312
- }, {
313
- ...options,
314
- skipLocatorHandlersCheckpoint
315
- });
316
- }
317
316
  async _performPointerAction(progress, actionName, waitForEnabled, action, forceScrollOptions, options) {
318
317
  const {
319
318
  force = false,
@@ -341,19 +340,8 @@ class ElementHandle extends js.JSHandle {
341
340
  await doScrollIntoView().catch(() => {});
342
341
  }
343
342
  if (options.__testHookBeforeStable) await options.__testHookBeforeStable();
344
- if (!force) {
345
- const elementStates = waitForEnabled ? ['visible', 'enabled', 'stable'] : ['visible', 'stable'];
346
- progress.log(` waiting for element to be ${waitForEnabled ? 'visible, enabled and stable' : 'visible and stable'}`);
347
- const result = await this.evaluateInUtility(async ([injected, node, {
348
- elementStates
349
- }]) => {
350
- return await injected.checkElementStates(node, elementStates);
351
- }, {
352
- elementStates
353
- });
354
- if (result) return result;
355
- progress.log(` element is ${waitForEnabled ? 'visible, enabled and stable' : 'visible and stable'}`);
356
- }
343
+ const result = await this._waitForElementStates(progress, waitForEnabled ? ['visible', 'enabled', 'stable'] : ['visible', 'stable'], force);
344
+ if (result !== 'done') return result;
357
345
  if (options.__testHookAfterStable) await options.__testHookAfterStable();
358
346
  progress.log(' scrolling into view if needed');
359
347
  progress.throwIfAborted(); // Avoid action that has side-effects.
@@ -366,9 +354,7 @@ class ElementHandle extends js.JSHandle {
366
354
  progress.metadata.point = point;
367
355
  await progress.beforeInputAction(this);
368
356
  let hitTargetInterceptionHandle;
369
- if (force) {
370
- progress.log(` forcing action`);
371
- } else {
357
+ if (!options.force) {
372
358
  if (options.__testHookBeforeHitTarget) await options.__testHookBeforeHitTarget();
373
359
  const frameCheckResult = await this._checkFrameIsHitTarget(point);
374
360
  if (frameCheckResult === 'error:notconnected' || 'hitTargetDescription' in frameCheckResult) return frameCheckResult;
@@ -476,32 +462,22 @@ class ElementHandle extends js.JSHandle {
476
462
  }, this._page._timeoutSettings.timeout(options));
477
463
  }
478
464
  async _selectOption(progress, elements, values, options) {
479
- let resultingOptions = [];
480
- await this._retryAction(progress, 'select option', async () => {
481
- await progress.beforeInputAction(this);
482
- if (!options.force) progress.log(` waiting for element to be visible and enabled`);
483
- const optionsToSelect = [...elements, ...values];
484
- const result = await this.evaluateInUtility(async ([injected, node, {
465
+ const optionsToSelect = [...elements, ...values];
466
+ await progress.beforeInputAction(this);
467
+ return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => {
468
+ progress.throwIfAborted(); // Avoid action that has side-effects.
469
+ progress.log(' selecting specified option(s)');
470
+ const result = await this.evaluatePoll(progress, ([injected, node, {
485
471
  optionsToSelect,
486
472
  force
487
473
  }]) => {
488
- if (!force) {
489
- const checkResult = await injected.checkElementStates(node, ['visible', 'enabled']);
490
- if (checkResult) return checkResult;
491
- }
492
- return injected.selectOptions(node, optionsToSelect);
474
+ return injected.waitForElementStatesAndPerformAction(node, ['visible', 'enabled'], force, injected.selectOptions.bind(injected, optionsToSelect));
493
475
  }, {
494
476
  optionsToSelect,
495
477
  force: options.force
496
478
  });
497
- if (Array.isArray(result)) {
498
- progress.log(' selected specified option(s)');
499
- resultingOptions = result;
500
- return 'done';
501
- }
502
479
  return result;
503
- }, options);
504
- return resultingOptions;
480
+ });
505
481
  }
506
482
  async fill(metadata, value, options = {}) {
507
483
  const controller = new _progress.ProgressController(metadata, this);
@@ -511,51 +487,38 @@ class ElementHandle extends js.JSHandle {
511
487
  }, this._page._timeoutSettings.timeout(options));
512
488
  }
513
489
  async _fill(progress, value, options) {
514
- progress.log(` fill("${value}")`);
515
- return await this._retryAction(progress, 'fill', async () => {
516
- await progress.beforeInputAction(this);
517
- return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => {
518
- if (!options.force) progress.log(' waiting for element to be visible, enabled and editable');
519
- const result = await this.evaluateInUtility(async ([injected, node, {
520
- value,
521
- force
522
- }]) => {
523
- if (!force) {
524
- const checkResult = await injected.checkElementStates(node, ['visible', 'enabled', 'editable']);
525
- if (checkResult) return checkResult;
526
- }
527
- return injected.fill(node, value);
528
- }, {
529
- value,
530
- force: options.force
531
- });
490
+ progress.log(`elementHandle.fill("${value}")`);
491
+ await progress.beforeInputAction(this);
492
+ return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => {
493
+ progress.log(' waiting for element to be visible, enabled and editable');
494
+ const filled = await this.evaluatePoll(progress, ([injected, node, {
495
+ value,
496
+ force
497
+ }]) => {
498
+ return injected.waitForElementStatesAndPerformAction(node, ['visible', 'enabled', 'editable'], force, injected.fill.bind(injected, value));
499
+ }, {
500
+ value,
501
+ force: options.force
502
+ });
503
+ progress.throwIfAborted(); // Avoid action that has side-effects.
504
+ if (filled === 'error:notconnected') return filled;
505
+ progress.log(' element is visible, enabled and editable');
506
+ if (filled === 'needsinput') {
532
507
  progress.throwIfAborted(); // Avoid action that has side-effects.
533
- if (result === 'needsinput') {
534
- if (value) await this._page.keyboard.insertText(value);else await this._page.keyboard.press('Delete');
535
- return 'done';
536
- } else {
537
- return result;
538
- }
539
- }, 'input');
540
- }, options);
508
+ if (value) await this._page.keyboard.insertText(value);else await this._page.keyboard.press('Delete');
509
+ } else {
510
+ assertDone(filled);
511
+ }
512
+ return 'done';
513
+ }, 'input');
541
514
  }
542
515
  async selectText(metadata, options = {}) {
543
516
  const controller = new _progress.ProgressController(metadata, this);
544
517
  return controller.run(async progress => {
545
- const result = await this._retryAction(progress, 'selectText', async () => {
546
- if (!options.force) progress.log(' waiting for element to be visible');
547
- return await this.evaluateInUtility(async ([injected, node, {
548
- force
549
- }]) => {
550
- if (!force) {
551
- const checkResult = await injected.checkElementStates(node, ['visible']);
552
- if (checkResult) return checkResult;
553
- }
554
- return injected.selectText(node);
555
- }, {
556
- force: options.force
557
- });
558
- }, options);
518
+ progress.throwIfAborted(); // Avoid action that has side-effects.
519
+ const result = await this.evaluatePoll(progress, ([injected, node, force]) => {
520
+ return injected.waitForElementStatesAndPerformAction(node, ['visible'], force, injected.selectText.bind(injected));
521
+ }, options.force);
559
522
  assertDone(throwRetargetableDOMError(result));
560
523
  }, this._page._timeoutSettings.timeout(options));
561
524
  }
@@ -706,12 +669,10 @@ class ElementHandle extends js.JSHandle {
706
669
  async waitForElementState(metadata, state, options = {}) {
707
670
  const controller = new _progress.ProgressController(metadata, this);
708
671
  return controller.run(async progress => {
709
- const actionName = `wait for ${state}`;
710
- const result = await this._retryAction(progress, actionName, async () => {
711
- return await this.evaluateInUtility(async ([injected, node, state]) => {
712
- return (await injected.checkElementStates(node, [state])) || 'done';
713
- }, state);
714
- }, {});
672
+ progress.log(` waiting for element to be ${state}`);
673
+ const result = await this.evaluatePoll(progress, ([injected, node, state]) => {
674
+ return injected.waitForElementStatesAndPerformAction(node, [state], false, () => 'done');
675
+ }, state);
715
676
  assertDone(throwRetargetableDOMError(result));
716
677
  }, this._page._timeoutSettings.timeout(options));
717
678
  }
@@ -726,6 +687,22 @@ class ElementHandle extends js.JSHandle {
726
687
  }
727
688
  return this;
728
689
  }
690
+ async _waitForElementStates(progress, states, force) {
691
+ const title = joinWithAnd(states);
692
+ progress.log(` waiting for element to be ${title}`);
693
+ const result = await this.evaluatePoll(progress, ([injected, node, {
694
+ states,
695
+ force
696
+ }]) => {
697
+ return injected.waitForElementStatesAndPerformAction(node, states, force, () => 'done');
698
+ }, {
699
+ states,
700
+ force
701
+ });
702
+ if (result === 'error:notconnected') return result;
703
+ progress.log(` element is ${title}`);
704
+ return result;
705
+ }
729
706
  async _checkFrameIsHitTarget(point) {
730
707
  let frame = this._frame;
731
708
  const data = [];
@@ -774,7 +751,63 @@ class ElementHandle extends js.JSHandle {
774
751
  };
775
752
  }
776
753
  }
754
+
755
+ // Handles an InjectedScriptPoll running in injected script:
756
+ // - streams logs into progress;
757
+ // - cancels the poll when progress cancels.
777
758
  exports.ElementHandle = ElementHandle;
759
+ class InjectedScriptPollHandler {
760
+ constructor(progress, poll) {
761
+ this._progress = void 0;
762
+ this._poll = void 0;
763
+ this._progress = progress;
764
+ this._poll = poll;
765
+ // Ensure we cancel the poll before progress aborts and returns:
766
+ // - no unnecessary work in the page;
767
+ // - no possible side effects after progress promise rejects.
768
+ this._progress.cleanupWhenAborted(() => this.cancel());
769
+ this._streamLogs();
770
+ }
771
+ async _streamLogs() {
772
+ while (this._poll && this._progress.isRunning()) {
773
+ const log = await this._poll.evaluate(poll => poll.takeNextLogs()).catch(e => []);
774
+ if (!this._poll || !this._progress.isRunning()) return;
775
+ for (const entry of log) this._progress.logEntry(entry);
776
+ }
777
+ }
778
+ async finishHandle() {
779
+ try {
780
+ const result = await this._poll.evaluateHandle(poll => poll.run());
781
+ await this._finishInternal();
782
+ return result;
783
+ } finally {
784
+ await this.cancel();
785
+ }
786
+ }
787
+ async finish() {
788
+ try {
789
+ const result = await this._poll.evaluate(poll => poll.run());
790
+ await this._finishInternal();
791
+ return result;
792
+ } finally {
793
+ await this.cancel();
794
+ }
795
+ }
796
+ async _finishInternal() {
797
+ if (!this._poll) return;
798
+ // Retrieve all the logs before continuing.
799
+ const log = await this._poll.evaluate(poll => poll.takeLastLogs()).catch(e => []);
800
+ for (const entry of log) this._progress.logEntry(entry);
801
+ }
802
+ async cancel() {
803
+ if (!this._poll) return;
804
+ const copy = this._poll;
805
+ this._poll = null;
806
+ await copy.evaluate(p => p.cancel()).catch(e => {});
807
+ copy.dispose();
808
+ }
809
+ }
810
+ exports.InjectedScriptPollHandler = InjectedScriptPollHandler;
778
811
  function throwRetargetableDOMError(result) {
779
812
  if (result === 'error:notconnected') throw new Error('Element is not attached to the DOM');
780
813
  return result;
@@ -805,4 +838,8 @@ function compensateHalfIntegerRoundingError(point) {
805
838
  const remainderY = point.y - Math.floor(point.y);
806
839
  if (remainderY > 0.49 && remainderY < 0.51) point.y -= 0.02;
807
840
  }
841
+ function joinWithAnd(strings) {
842
+ if (strings.length <= 1) return strings.join('');
843
+ return strings.slice(0, strings.length - 1).join(', ') + ' and ' + strings[strings.length - 1];
844
+ }
808
845
  const kUnableToAdoptErrorMessage = exports.kUnableToAdoptErrorMessage = 'Unable to adopt element handle from a different document';
@@ -20,10 +20,8 @@ var _progress = require("../progress");
20
20
  var _helper = require("../helper");
21
21
  var _eventsHelper = require("../../utils/eventsHelper");
22
22
  var readline = _interopRequireWildcard(require("readline"));
23
- var _debugLogger = require("../../utils/debugLogger");
23
+ var _debugLogger = require("../../common/debugLogger");
24
24
  var _instrumentation = require("../instrumentation");
25
- var _crProtocolHelper = require("../chromium/crProtocolHelper");
26
- var _console = require("../console");
27
25
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
28
26
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
29
27
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -56,6 +54,10 @@ class ElectronApplication extends _instrumentation.SdkObject {
56
54
  this._process = void 0;
57
55
  this._process = process;
58
56
  this._browserContext = browser._defaultContext;
57
+ this._browserContext.on(_browserContext.BrowserContext.Events.Close, () => {
58
+ // Emit application closed after context closed.
59
+ Promise.resolve().then(() => this.emit(ElectronApplication.Events.Close));
60
+ });
59
61
  this._nodeConnection = nodeConnection;
60
62
  this._nodeSession = nodeConnection.rootSession;
61
63
  this._nodeSession.on('Runtime.executionContextCreated', async event => {
@@ -72,40 +74,14 @@ class ElectronApplication extends _instrumentation.SdkObject {
72
74
  });
73
75
  this._nodeElectronHandlePromise.resolve(new js.JSHandle(this._nodeExecutionContext, 'object', 'ElectronModule', remoteObject.objectId));
74
76
  });
75
- this._nodeSession.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
76
- const appClosePromise = new Promise(f => this.once(ElectronApplication.Events.Close, f));
77
77
  this._browserContext.setCustomCloseHandler(async () => {
78
78
  await this._browserContext.stopVideoRecording();
79
79
  const electronHandle = await this._nodeElectronHandlePromise;
80
80
  await electronHandle.evaluate(({
81
81
  app
82
82
  }) => app.quit()).catch(() => {});
83
- this._nodeConnection.close();
84
- await appClosePromise;
85
83
  });
86
84
  }
87
- async _onConsoleAPI(event) {
88
- if (event.executionContextId === 0) {
89
- // DevTools protocol stores the last 1000 console messages. These
90
- // messages are always reported even for removed execution contexts. In
91
- // this case, they are marked with executionContextId = 0 and are
92
- // reported upon enabling Runtime agent.
93
- //
94
- // Ignore these messages since:
95
- // - there's no execution context we can use to operate with message
96
- // arguments
97
- // - these messages are reported before Playwright clients can subscribe
98
- // to the 'console'
99
- // page event.
100
- //
101
- // @see https://github.com/GoogleChrome/puppeteer/issues/3865
102
- return;
103
- }
104
- if (!this._nodeExecutionContext) return;
105
- const args = event.args.map(arg => this._nodeExecutionContext.createHandle(arg));
106
- const message = new _console.ConsoleMessage(null, event.type, undefined, args, (0, _crProtocolHelper.toConsoleMessageLocation)(event.stackTrace));
107
- this.emit(ElectronApplication.Events.Console, message);
108
- }
109
85
  async initialize() {
110
86
  await this._nodeSession.send('Runtime.enable', {});
111
87
  // Delay loading the app until browser is started and the browser targets are configured to auto-attach.
@@ -120,10 +96,13 @@ class ElectronApplication extends _instrumentation.SdkObject {
120
96
  return this._browserContext;
121
97
  }
122
98
  async close() {
123
- // This will call BrowserContext.setCustomCloseHandler.
99
+ const progressController = new _progress.ProgressController((0, _instrumentation.serverSideCallMetadata)(), this);
100
+ const closed = progressController.run(progress => _helper.helper.waitForEvent(progress, this, ElectronApplication.Events.Close).promise);
124
101
  await this._browserContext.close({
125
102
  reason: 'Application exited'
126
103
  });
104
+ this._nodeConnection.close();
105
+ await closed;
127
106
  }
128
107
  async browserWindow(page) {
129
108
  // Assume CRPage as Electron is always Chromium.
@@ -140,8 +119,7 @@ class ElectronApplication extends _instrumentation.SdkObject {
140
119
  }
141
120
  exports.ElectronApplication = ElectronApplication;
142
121
  ElectronApplication.Events = {
143
- Close: 'close',
144
- Console: 'console'
122
+ Close: 'close'
145
123
  };
146
124
  class Electron extends _instrumentation.SdkObject {
147
125
  constructor(playwright) {
@@ -155,11 +133,10 @@ class Electron extends _instrumentation.SdkObject {
155
133
  controller.setLogName('browser');
156
134
  return controller.run(async progress => {
157
135
  let app = undefined;
158
- // --remote-debugging-port=0 must be the last playwright's argument, loader.ts relies on it.
159
136
  const electronArguments = ['--inspect=0', '--remote-debugging-port=0', ...args];
160
137
  if (_os.default.platform() === 'linux') {
161
138
  const runningAsRoot = process.geteuid && process.geteuid() === 0;
162
- if (runningAsRoot && electronArguments.indexOf('--no-sandbox') === -1) electronArguments.unshift('--no-sandbox');
139
+ if (runningAsRoot && electronArguments.indexOf('--no-sandbox') === -1) electronArguments.push('--no-sandbox');
163
140
  }
164
141
  const artifactsDir = await _fs.default.promises.mkdtemp(ARTIFACTS_FOLDER);
165
142
  const browserLogsCollector = new _debugLogger.RecentLogsCollector();
@@ -206,10 +183,7 @@ class Electron extends _instrumentation.SdkObject {
206
183
  handleSIGINT: true,
207
184
  handleSIGTERM: true,
208
185
  handleSIGHUP: true,
209
- onExit: () => {
210
- var _app;
211
- return (_app = app) === null || _app === void 0 ? void 0 : _app.emit(ElectronApplication.Events.Close);
212
- }
186
+ onExit: () => {}
213
187
  });
214
188
  const waitForXserverError = new Promise(async (resolve, reject) => {
215
189
  waitForLine(progress, launchedProcess, /Unable to open X display/).then(() => reject(new Error(['Unable to open X display!', `================================`, 'Most likely this is because there is no X server available.', "Use 'xvfb-run' on Linux to launch your tests with an emulated display server.", "For example: 'xvfb-run npm run test:e2e'", `================================`, progress.metadata.log].join('\n')))).catch(() => {});
@@ -23,10 +23,8 @@ const {
23
23
  chromiumSwitches
24
24
  } = require('../chromium/chromiumSwitches');
25
25
 
26
- // Always pass user arguments first, see https://github.com/microsoft/playwright/issues/16614 and
27
- // https://github.com/microsoft/playwright/issues/29198.
28
- // [Electron, -r, loader.js[, --no-sandbox>], --inspect=0, --remote-debugging-port=0, ...args]
29
- process.argv.splice(1, process.argv.indexOf('--remote-debugging-port=0'));
26
+ // [Electron, -r, loader.js, --inspect=0, --remote-debugging-port=0, ...args]
27
+ process.argv.splice(1, 4);
30
28
  for (const arg of chromiumSwitches) {
31
29
  const match = arg.match(/--([^=]*)=?(.*)/);
32
30
  app.commandLine.appendSwitch(match[1], match[2]);
@@ -200,7 +200,7 @@ class FFAXNode {
200
200
  if (!(numericalProperty in this._payload)) continue;
201
201
  node[numericalProperty] = this._payload[numericalProperty];
202
202
  }
203
- const tokenProperties = ['autocomplete', 'haspopup', 'orientation'];
203
+ const tokenProperties = ['autocomplete', 'haspopup', 'invalid', 'orientation'];
204
204
  for (const tokenProperty of tokenProperties) {
205
205
  const value = this._payload[tokenProperty];
206
206
  if (!value || value === 'false') continue;
@@ -210,7 +210,6 @@ class FFAXNode {
210
210
  axNode.valueString = this._payload.value;
211
211
  if ('checked' in this._payload) axNode.checked = this._payload.checked === true ? 'checked' : this._payload.checked === 'mixed' ? 'mixed' : 'unchecked';
212
212
  if ('pressed' in this._payload) axNode.pressed = this._payload.pressed === true ? 'pressed' : 'released';
213
- if ('invalid' in this._payload) axNode.invalid = this._payload.invalid === true ? 'true' : 'false';
214
213
  return axNode;
215
214
  }
216
215
  }
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.kBrowserCloseMessageId = exports.FFSession = exports.FFConnection = exports.ConnectionEvents = void 0;
7
7
  var _events = require("events");
8
- var _debugLogger = require("../../utils/debugLogger");
8
+ var _debugLogger = require("../../common/debugLogger");
9
9
  var _helper = require("../helper");
10
10
  var _protocolError = require("../protocolError");
11
11
  /**
@@ -14,7 +14,7 @@ var _ffExecutionContext = require("./ffExecutionContext");
14
14
  var _ffInput = require("./ffInput");
15
15
  var _ffNetworkManager = require("./ffNetworkManager");
16
16
  var _stackTrace = require("../../utils/stackTrace");
17
- var _debugLogger = require("../../utils/debugLogger");
17
+ var _debugLogger = require("../../common/debugLogger");
18
18
  var _manualPromise = require("../../utils/manualPromise");
19
19
  var _browserContext = require("../browserContext");
20
20
  var _errors = require("../errors");