@smoothdeploy/playwright 1.63.0-beta-1791031101000 → 1.63.0-beta-1794047129000

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.

Potentially problematic release.


This version of @smoothdeploy/playwright might be problematic. Click here for more details.

package/lib/index.js CHANGED
@@ -273,7 +273,7 @@ const playwrightFixtures = {
273
273
  clientCertificates: [({ contextOptions }, use) => use(contextOptions.clientCertificates), { option: true, box: true }],
274
274
  timezoneId: [({ contextOptions }, use) => use(contextOptions.timezoneId), { option: true, box: true }],
275
275
  userAgent: [({ contextOptions }, use) => use(contextOptions.userAgent), { option: true, box: true }],
276
- viewport: [({ contextOptions }, use) => use(contextOptions.viewport === void 0 ? null : contextOptions.viewport), { option: true, box: true }],
276
+ viewport: [({ contextOptions }, use) => use(contextOptions.viewport === void 0 ? { width: 1280, height: 720 } : contextOptions.viewport), { option: true, box: true }],
277
277
  actionTimeout: [0, { option: true, box: true }],
278
278
  navigationTimeout: [0, { option: true, box: true }],
279
279
  baseURL: [async ({}, use) => {
@@ -382,6 +382,7 @@ const playwrightFixtures = {
382
382
  _contextFactory: [async ({
383
383
  browser,
384
384
  video,
385
+ resultScreenshots,
385
386
  _reuseContext,
386
387
  _combinedContextOptions
387
388
  /** mitigate dep-via-auto lack of traceability */
@@ -404,7 +405,8 @@ const playwrightFixtures = {
404
405
  const videoOptions = captureVideo ? {
405
406
  recordVideo: {
406
407
  dir: tracing().artifactsDir(),
407
- size: typeof video === "string" ? void 0 : video.size,
408
+ // Video locks the shared screencast size, so default it to the viewport to keep result screenshots at full resolution.
409
+ size: (typeof video === "string" ? void 0 : video.size) ?? (resultScreenshots ? options?.viewport ?? _combinedContextOptions.viewport ?? void 0 : void 0),
408
410
  showActions: show?.actions
409
411
  }
410
412
  } : {};
@@ -752,6 +754,7 @@ class ArtifactsRecorder {
752
754
  }
753
755
  async didFinishTest() {
754
756
  await this.didFinishTestFunction();
757
+ await Promise.all(_pendingResultScreenshots.splice(0));
755
758
  const leftoverContexts = this._playwright._allContexts();
756
759
  const leftoverApiRequests = Array.from(this._playwright.request._contexts);
757
760
  await Promise.all(leftoverContexts.map(async (context2) => {
@@ -814,6 +817,34 @@ class ArtifactsRecorder {
814
817
  }
815
818
  }
816
819
  let _capturingResultScreenshot = false;
820
+ const _pendingResultScreenshots = [];
821
+ const _screencastFrames = /* @__PURE__ */ new WeakMap();
822
+ async function latestScreencastFrame(page, size, after) {
823
+ let state = _screencastFrames.get(page);
824
+ if (!state) {
825
+ const s = { timestamp: 0 };
826
+ state = s;
827
+ _screencastFrames.set(page, s);
828
+ await page._wrapApiCall(() => page.screencast.start({
829
+ size: size ?? void 0,
830
+ quality: 90,
831
+ onFrame: (frame) => {
832
+ s.data = frame.data;
833
+ s.timestamp = frame.timestamp;
834
+ s.onFrame?.();
835
+ }
836
+ }), { internal: true });
837
+ }
838
+ const start = Date.now();
839
+ const deadline = () => start + (state.data ? 100 : 2e3);
840
+ while ((!state.data || state.timestamp < after) && Date.now() < deadline()) {
841
+ await new Promise((resolve) => {
842
+ state.onFrame = resolve;
843
+ setTimeout(resolve, deadline() - Date.now());
844
+ });
845
+ }
846
+ return state.data;
847
+ }
817
848
  async function captureResultScreenshot(channel, data, testInfo) {
818
849
  if (_capturingResultScreenshot)
819
850
  return;
@@ -840,25 +871,23 @@ async function captureResultScreenshot(channel, data, testInfo) {
840
871
  const cssViewport = await page._wrapApiCall(async () => {
841
872
  return await frame.evaluate(() => ({ width: window.innerWidth, height: window.innerHeight }));
842
873
  }, { internal: true }).catch(() => null);
843
- const pngBuffer = await page._wrapApiCall(async () => {
844
- return await page.screenshot({
845
- type: "png",
846
- animations: "disabled",
847
- timeout: 5e3,
848
- style: "x-pw-glass { display: none !important; }"
849
- });
850
- }, { internal: true });
851
- const { full, cropped } = import_coreBundle.server.compositeHighlight(pngBuffer, box, cssViewport);
874
+ const jpeg = await latestScreencastFrame(page, cssViewport, Date.now());
875
+ if (!jpeg)
876
+ return;
852
877
  const safeTestId = testInfo.testId.replace(/[^a-zA-Z0-9_-]/g, "-");
853
878
  const safeStepId = (data.stepId ?? "unknown").replace(/[^a-zA-Z0-9_-]/g, "-");
854
879
  const screenshotDir = import_path.default.join(process.cwd(), ".smoothdeploy-output", "screenshots", safeTestId);
855
- import_fs.default.mkdirSync(screenshotDir, { recursive: true });
856
- const fullPng = new import_utilsBundle.PNG({ width: full.width, height: full.height });
857
- fullPng.data = full.data;
858
- import_fs.default.writeFileSync(import_path.default.join(screenshotDir, `${safeStepId}-page.png`), import_utilsBundle.PNG.sync.write(fullPng));
859
- const croppedPng = new import_utilsBundle.PNG({ width: cropped.width, height: cropped.height });
860
- croppedPng.data = cropped.data;
861
- import_fs.default.writeFileSync(import_path.default.join(screenshotDir, `${safeStepId}-element.png`), import_utilsBundle.PNG.sync.write(croppedPng));
880
+ _pendingResultScreenshots.push(new Promise((resolve) => setImmediate(resolve)).then(async () => {
881
+ const { full, cropped } = import_coreBundle.server.compositeHighlight(jpeg, box, cssViewport);
882
+ await import_fs.default.promises.mkdir(screenshotDir, { recursive: true });
883
+ const encode = (image) => {
884
+ const png = new import_utilsBundle.PNG({ width: image.width, height: image.height });
885
+ png.data = image.data;
886
+ return import_utilsBundle.PNG.sync.write(png);
887
+ };
888
+ await import_fs.default.promises.writeFile(import_path.default.join(screenshotDir, `${safeStepId}-page.png`), encode(full));
889
+ await import_fs.default.promises.writeFile(import_path.default.join(screenshotDir, `${safeStepId}-element.png`), encode(cropped));
890
+ }).catch((e) => debugLogger.log("error", `failed to write result screenshot: ${e}`)));
862
891
  } catch {
863
892
  } finally {
864
893
  _capturingResultScreenshot = false;
@@ -5308,7 +5308,7 @@ var SmoothDeployReporter = class {
5308
5308
  this._retries = /* @__PURE__ */ new WeakMap();
5309
5309
  this._rootCursors = /* @__PURE__ */ new Map();
5310
5310
  this._testSteps = /* @__PURE__ */ new Map();
5311
- this._testIdMap = /* @__PURE__ */ new Map();
5311
+ this._projectNames = [];
5312
5312
  this._stepIdMap = /* @__PURE__ */ new Map();
5313
5313
  }
5314
5314
  version() {
@@ -5319,6 +5319,7 @@ var SmoothDeployReporter = class {
5319
5319
  }
5320
5320
  onConfigure(config2) {
5321
5321
  this._configDir = config2.configFile ? path13.dirname(config2.configFile) : config2.rootDir;
5322
+ this._projectNames = config2.projects.map((p) => p.name);
5322
5323
  const manifestPath = path13.join(this._configDir, ".smoothdeploy-manifest.json");
5323
5324
  if (fs10.existsSync(manifestPath))
5324
5325
  this._manifest = JSON.parse(fs10.readFileSync(manifestPath, "utf-8"));
@@ -5340,13 +5341,14 @@ var SmoothDeployReporter = class {
5340
5341
  return void 0;
5341
5342
  }
5342
5343
  _resolveManifestTest(test) {
5344
+ const projectName = test.parent.project()?.name ?? "";
5343
5345
  const manifestTest = this._findManifestTest(test);
5344
5346
  if (!manifestTest)
5345
- return { manifestTest: { id: test.id, title: test.title, steps: [] }, paramIndex: 0, passthrough: true };
5346
- const single = manifestTest.manifestTest.steps.length === 1 ? manifestTest.manifestTest.steps[0] : void 0;
5347
+ return { manifestTest: { id: test.id, title: test.title, steps: [] }, projectName, passthrough: true };
5348
+ const single = manifestTest.steps.length === 1 ? manifestTest.steps[0] : void 0;
5347
5349
  if (single && single.action === "script")
5348
- return { ...manifestTest, passthrough: true, passthroughRootId: single.id };
5349
- return { ...manifestTest, passthrough: false };
5350
+ return { manifestTest, projectName, passthrough: true, passthroughRootId: single.id };
5351
+ return { manifestTest, projectName, passthrough: false };
5350
5352
  }
5351
5353
  _findManifestTest(test) {
5352
5354
  if (!this._manifest)
@@ -5367,29 +5369,17 @@ var SmoothDeployReporter = class {
5367
5369
  currentSuites = suite.suites;
5368
5370
  currentTests = suite.tests;
5369
5371
  }
5370
- const exactMatch = currentTests.find((t2) => t2.title === test.title);
5371
- if (exactMatch)
5372
- return { manifestTest: exactMatch, paramIndex: 0 };
5373
- for (const mt of currentTests) {
5374
- if (!mt.params || mt.params < 1 || !test.title.startsWith(mt.title))
5375
- continue;
5376
- const suffix = test.title.slice(mt.title.length);
5377
- const idx = parseInt(suffix, 10);
5378
- if (String(idx) === suffix && idx >= 0 && idx < mt.params)
5379
- return { manifestTest: mt, paramIndex: idx };
5380
- }
5381
- return null;
5372
+ return currentTests.find((t2) => t2.title === test.title) ?? null;
5382
5373
  }
5383
5374
  onTestBegin(test, result) {
5384
5375
  const resolved = this._resolveManifestTest(test);
5385
5376
  this._resolvedTests.set(test.id, resolved);
5386
- this._testIdMap.set(safeId(test.id), safeId(resolved.manifestTest.id));
5387
5377
  this._rootCursors.set(test.id, 0);
5388
5378
  this._testSteps.set(test.id, []);
5389
5379
  this._emit({
5390
5380
  event: "testBegin",
5391
5381
  testId: resolved.manifestTest.id,
5392
- paramIndex: resolved.paramIndex,
5382
+ projectName: resolved.projectName,
5393
5383
  testTitle: test.title,
5394
5384
  timestamp: Date.now()
5395
5385
  });
@@ -5397,7 +5387,7 @@ var SmoothDeployReporter = class {
5397
5387
  this._emit({
5398
5388
  event: "stepBegin",
5399
5389
  testId: resolved.manifestTest.id,
5400
- paramIndex: resolved.paramIndex,
5390
+ projectName: resolved.projectName,
5401
5391
  id: resolved.passthroughRootId,
5402
5392
  action: "script",
5403
5393
  title: test.title,
@@ -5414,7 +5404,7 @@ var SmoothDeployReporter = class {
5414
5404
  this._emit({
5415
5405
  event: "stepEnd",
5416
5406
  testId: resolved.manifestTest.id,
5417
- paramIndex: resolved.paramIndex,
5407
+ projectName: resolved.projectName,
5418
5408
  id: resolved.passthroughRootId,
5419
5409
  action: "script",
5420
5410
  title: test.title,
@@ -5426,7 +5416,7 @@ var SmoothDeployReporter = class {
5426
5416
  this._emit({
5427
5417
  event: "testEnd",
5428
5418
  testId: resolved.manifestTest.id,
5429
- paramIndex: resolved.paramIndex,
5419
+ projectName: resolved.projectName,
5430
5420
  testTitle: test.title,
5431
5421
  status: result.status,
5432
5422
  duration: result.duration,
@@ -5521,7 +5511,7 @@ var SmoothDeployReporter = class {
5521
5511
  this._emit({
5522
5512
  event: "stepBegin",
5523
5513
  testId: resolved.manifestTest.id,
5524
- paramIndex: resolved.paramIndex,
5514
+ projectName: resolved.projectName,
5525
5515
  id: manifestStep.id,
5526
5516
  action: manifestStep.action,
5527
5517
  title: step.title,
@@ -5530,7 +5520,7 @@ var SmoothDeployReporter = class {
5530
5520
  }, step);
5531
5521
  }
5532
5522
  _screenshotsFor(test, step) {
5533
- const hasScreenshot = step.category === "pw:api" && !NO_SCREENSHOT_PREFIXES.some((prefix) => step.title.startsWith(prefix));
5523
+ const hasScreenshot = (step.category === "pw:api" || step.category === "expect") && !NO_SCREENSHOT_PREFIXES.some((prefix) => step.title.startsWith(prefix));
5534
5524
  if (!hasScreenshot)
5535
5525
  return null;
5536
5526
  const safeStepId = safeId(step._stepId ?? step.title);
@@ -5578,7 +5568,7 @@ var SmoothDeployReporter = class {
5578
5568
  this._emit({
5579
5569
  event: "stepEnd",
5580
5570
  testId: resolved.manifestTest.id,
5581
- paramIndex: resolved.paramIndex,
5571
+ projectName: resolved.projectName,
5582
5572
  id: matched.manifest.id,
5583
5573
  action: matched.manifest.action,
5584
5574
  title: step.title,
@@ -5591,8 +5581,9 @@ var SmoothDeployReporter = class {
5591
5581
  onEnd(result) {
5592
5582
  const outputDir = path13.join(process.cwd(), ".smoothdeploy-output");
5593
5583
  const screenshotsDir = path13.join(outputDir, "screenshots");
5594
- for (const [safePwTestId] of this._testIdMap) {
5595
- const testDir = path13.join(screenshotsDir, safePwTestId);
5584
+ const screenshotDirs = this._screenshotDirs();
5585
+ for (const testId of screenshotDirs.keys()) {
5586
+ const testDir = path13.join(screenshotsDir, safeId(testId));
5596
5587
  if (!fs10.existsSync(testDir))
5597
5588
  continue;
5598
5589
  for (const [safePwStepId, safeManifestStepId] of this._stepIdMap) {
@@ -5603,22 +5594,26 @@ var SmoothDeployReporter = class {
5603
5594
  }
5604
5595
  }
5605
5596
  }
5606
- for (const [safePwTestId, safeManifestTestId] of this._testIdMap) {
5607
- const src = path13.join(screenshotsDir, safePwTestId);
5608
- const dst = path13.join(screenshotsDir, safeManifestTestId);
5597
+ const byDepth = [...screenshotDirs].sort(([, a], [, b]) => a.split("/").length - b.split("/").length);
5598
+ for (const [testId, dir] of byDepth) {
5599
+ const src = path13.join(screenshotsDir, safeId(testId));
5600
+ const dst = path13.join(screenshotsDir, dir);
5609
5601
  if (src !== dst && fs10.existsSync(src)) {
5610
5602
  if (fs10.existsSync(dst))
5611
5603
  fs10.rmSync(dst, { recursive: true });
5604
+ fs10.mkdirSync(path13.dirname(dst), { recursive: true });
5612
5605
  fs10.renameSync(src, dst);
5613
5606
  }
5614
5607
  }
5615
- const remapScreenshotPaths = (steps) => {
5608
+ const remapScreenshotPaths = (steps, testId, dir) => {
5616
5609
  for (const step of steps) {
5617
5610
  if (step.screenshots) {
5618
- step.screenshots.page = this._remapPath(step.screenshots.page);
5619
- step.screenshots.element = this._remapPath(step.screenshots.element);
5611
+ step.screenshots.page = this._remapPath(step.screenshots.page, testId, dir);
5612
+ step.screenshots.element = this._remapPath(step.screenshots.element, testId, dir);
5613
+ if (!fs10.existsSync(path13.join(process.cwd(), step.screenshots.page)))
5614
+ step.screenshots = null;
5620
5615
  }
5621
- remapScreenshotPaths(step.children);
5616
+ remapScreenshotPaths(step.children, testId, dir);
5622
5617
  }
5623
5618
  };
5624
5619
  const manifest = {};
@@ -5626,23 +5621,36 @@ var SmoothDeployReporter = class {
5626
5621
  const resolved = this._resolvedTests.get(testId);
5627
5622
  if (!resolved)
5628
5623
  continue;
5629
- remapScreenshotPaths(steps);
5630
- const safeManifestTestId = safeId(resolved.manifestTest.id);
5631
- const failurePath = path13.join(screenshotsDir, safeManifestTestId, "failure.png");
5632
- const failureScreenshot = fs10.existsSync(failurePath) ? `.smoothdeploy-output/screenshots/${safeManifestTestId}/failure.png` : null;
5624
+ const dir = screenshotDirs.get(testId);
5625
+ remapScreenshotPaths(steps, testId, dir);
5626
+ const failureScreenshot = fs10.existsSync(path13.join(screenshotsDir, dir, "failure.png")) ? `.smoothdeploy-output/screenshots/${dir}/failure.png` : null;
5633
5627
  const key = resolved.manifestTest.id;
5634
5628
  if (!manifest[key])
5635
5629
  manifest[key] = [];
5636
- manifest[key].push({ paramIndex: resolved.paramIndex, steps, failureScreenshot });
5630
+ manifest[key].push({ projectName: resolved.projectName, steps, failureScreenshot });
5637
5631
  }
5638
5632
  fs10.mkdirSync(outputDir, { recursive: true });
5639
5633
  fs10.writeFileSync(path13.join(outputDir, "manifest.json"), JSON.stringify(manifest, null, 2));
5640
5634
  this._emit({ event: "runEnd", status: result.status, timestamp: Date.now() });
5641
5635
  }
5642
- _remapPath(screenshotPath) {
5643
- let result = screenshotPath;
5644
- for (const [safePwTestId, safeManifestTestId] of this._testIdMap)
5645
- result = result.replace(`/${safePwTestId}/`, `/${safeManifestTestId}/`);
5636
+ // The first configured project keeps screenshots/<testId>/, which the console builds remote-run URLs from; other projects get a subfolder.
5637
+ _screenshotDirs() {
5638
+ const primary = /* @__PURE__ */ new Map();
5639
+ for (const [testId, resolved] of this._resolvedTests) {
5640
+ const rank = this._projectNames.indexOf(resolved.projectName);
5641
+ const current = primary.get(resolved.manifestTest.id);
5642
+ if (!current || rank < current.rank)
5643
+ primary.set(resolved.manifestTest.id, { testId, rank });
5644
+ }
5645
+ const dirs = /* @__PURE__ */ new Map();
5646
+ for (const [testId, resolved] of this._resolvedTests) {
5647
+ const base = safeId(resolved.manifestTest.id);
5648
+ dirs.set(testId, primary.get(resolved.manifestTest.id).testId === testId ? base : `${base}/${safeId(resolved.projectName) || "default"}`);
5649
+ }
5650
+ return dirs;
5651
+ }
5652
+ _remapPath(screenshotPath, testId, dir) {
5653
+ let result = screenshotPath.replace(`/${safeId(testId)}/`, `/${dir}/`);
5646
5654
  for (const [safePwStepId, safeManifestStepId] of this._stepIdMap)
5647
5655
  result = result.replace(`/${safePwStepId}-`, `/${safeManifestStepId}-`);
5648
5656
  return result;
@@ -1,5 +1,5 @@
1
1
  # packages/playwright/lib/runner/index.js
2
- # total: 324.8 KB
2
+ # total: 325.4 KB
3
3
 
4
4
  ## Inlined (49)
5
5
  1.3 KB packages/playwright/src/isomorphic/events.ts
@@ -27,7 +27,7 @@
27
27
  3.0 KB packages/playwright/src/reporters/multiplexer.ts
28
28
  8.9 KB packages/playwright/src/reporters/perfetto.ts
29
29
  2.1 KB packages/playwright/src/reporters/reporterV2.ts
30
- 13.3 KB packages/playwright/src/reporters/smoothdeploy.ts
30
+ 13.9 KB packages/playwright/src/reporters/smoothdeploy.ts
31
31
  3.1 KB packages/playwright/src/reporters/smoothdeployMatch.ts
32
32
  8.8 KB packages/playwright/src/reporters/teleEmitter.ts
33
33
  20.6 KB packages/playwright/src/runner/dispatcher.ts
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@smoothdeploy/playwright",
3
- "version": "1.63.0-beta-1791031101000",
3
+ "version": "1.63.0-beta-1794047129000",
4
4
  "description": "A high-level API to automate web browsers",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/microsoft/playwright.git"
8
8
  },
9
- "homepage": "https://playwright.dev",
9
+ "homepage": "https://smoothdeploy.com",
10
10
  "engines": {
11
11
  "node": ">=20"
12
12
  },
@@ -50,6 +50,6 @@
50
50
  },
51
51
  "license": "Apache-2.0",
52
52
  "dependencies": {
53
- "playwright-core": "npm:@smoothdeploy/playwright-core@1.63.0-beta-1791031101000"
53
+ "playwright-core": "npm:@smoothdeploy/playwright-core@1.63.0-beta-1794047129000"
54
54
  }
55
55
  }