@smoothdeploy/playwright 1.60.1 → 1.61.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/lib/index.js CHANGED
@@ -402,16 +402,6 @@ const playwrightFixtures = {
402
402
  }
403
403
  } : {};
404
404
  const context = await browser.newContext({ ...videoOptions, ...options });
405
- if (process.env.PW_CLOCK === "frozen") {
406
- await context._wrapApiCall(async () => {
407
- await context.clock.install({ time: 0 });
408
- await context.clock.pauseAt(1e3);
409
- }, { internal: true });
410
- } else if (process.env.PW_CLOCK === "realtime") {
411
- await context._wrapApiCall(async () => {
412
- await context.clock.install({ time: 0 });
413
- }, { internal: true });
414
- }
415
405
  let closed = false;
416
406
  const close = async () => {
417
407
  if (closed)
@@ -419,8 +409,7 @@ const playwrightFixtures = {
419
409
  closed = true;
420
410
  const closeReason = testInfo.status === "timedOut" ? "Test timeout of " + testInfo.timeout + "ms exceeded." : "Test ended.";
421
411
  await context.close({ reason: closeReason });
422
- const testFailed = testInfo.status !== testInfo.expectedStatus;
423
- const preserveVideo = captureVideo && (videoMode === "on" || testFailed && videoMode === "retain-on-failure" || videoMode === "on-first-retry" && testInfo.retry === 1);
412
+ const preserveVideo = captureVideo && shouldPreserveVideo(videoMode, testInfo);
424
413
  if (preserveVideo) {
425
414
  const { pagesWithVideo: pagesForVideo } = contexts.get(context);
426
415
  const videos = pagesForVideo.map((p) => p.video()).filter((video2) => !!video2);
@@ -490,7 +479,23 @@ function normalizeVideoMode(video) {
490
479
  return videoMode;
491
480
  }
492
481
  function shouldCaptureVideo(videoMode, testInfo) {
493
- return videoMode === "on" || videoMode === "retain-on-failure" || videoMode === "on-first-retry" && testInfo.retry === 1;
482
+ return videoMode === "on" || videoMode === "retain-on-failure" || videoMode === "retain-on-failure-and-retries" || videoMode === "on-first-retry" && testInfo.retry === 1 || videoMode === "on-all-retries" && testInfo.retry > 0 || videoMode === "retain-on-first-failure" && testInfo.retry === 0;
483
+ }
484
+ function shouldPreserveVideo(videoMode, testInfo) {
485
+ const testFailed = testInfo.status !== testInfo.expectedStatus;
486
+ switch (videoMode) {
487
+ case "on":
488
+ case "on-first-retry":
489
+ case "on-all-retries":
490
+ return true;
491
+ case "retain-on-failure":
492
+ case "retain-on-first-failure":
493
+ return testFailed;
494
+ case "retain-on-failure-and-retries":
495
+ return testFailed || testInfo.retry > 0;
496
+ default:
497
+ return false;
498
+ }
494
499
  }
495
500
  function normalizeScreenshotMode(screenshot) {
496
501
  if (!screenshot)
@@ -24,7 +24,7 @@ var LoaderMain = class extends import_common.ProcessRunner {
24
24
  return { fileSuite: fileSuite._deepSerialize(), testErrors };
25
25
  }
26
26
  async getCompilationCacheFromLoader() {
27
- await import_common.esm.incorporateCompilationCache();
27
+ await import_common.transform.incorporateCompilationCache();
28
28
  return import_common.cc.serializeCompilationCache();
29
29
  }
30
30
  };
@@ -11976,8 +11976,7 @@ var NonConfigProperties = [
11976
11976
  "fullPage",
11977
11977
  "mask",
11978
11978
  "maskColor",
11979
- "omitBackground",
11980
- "timeout"
11979
+ "omitBackground"
11981
11980
  ];
11982
11981
  var SnapshotHelper = class {
11983
11982
  constructor(state, testInfo, matcherName, locator, anonymousSnapshotExtension, configOptions, nameOrOptions, optOptions) {
@@ -12596,6 +12595,7 @@ var import_fs2 = __toESM(require("fs"));
12596
12595
  var import_path2 = __toESM(require("path"));
12597
12596
  var { escapeTemplateString, isString: isString3 } = require("playwright-core/lib/coreBundle").iso;
12598
12597
  var { existsAsync } = require("playwright-core/lib/coreBundle").utils;
12598
+ var kImpossibleAriaMatch = `- none "Generating new baseline"`;
12599
12599
  async function toMatchAriaSnapshot(receiver, expectedParam, options = {}) {
12600
12600
  const matcherName = "toMatchAriaSnapshot";
12601
12601
  expectTypes(receiver, ["Page", "Locator"], matcherName);
@@ -12620,14 +12620,14 @@ async function toMatchAriaSnapshot(receiver, expectedParam, options = {}) {
12620
12620
  expected = await import_fs2.default.promises.readFile(expectedPath, "utf8").catch(() => "");
12621
12621
  timeout = expectedParam?.timeout ?? this.timeout;
12622
12622
  }
12623
- const generateMissingBaseline = updateSnapshots === "missing" && !expected;
12624
- if (generateMissingBaseline) {
12625
- if (this.isNot) {
12626
- const message2 = `Matchers using ".not" can't generate new baselines`;
12627
- return { pass: this.isNot, message: () => message2, name: "toMatchAriaSnapshot" };
12628
- } else {
12629
- expected = `- none "Generating new baseline"`;
12630
- }
12623
+ const isMissingBaseline = updateSnapshots === "missing" && !expected;
12624
+ if (isMissingBaseline && this.isNot) {
12625
+ const message2 = `Matchers using ".not" can't generate new baselines`;
12626
+ return { pass: this.isNot, message: () => message2, name: "toMatchAriaSnapshot" };
12627
+ }
12628
+ const generateBaseline = !this.isNot && (updateSnapshots === "all" || isMissingBaseline);
12629
+ if (generateBaseline) {
12630
+ expected = kImpossibleAriaMatch;
12631
12631
  }
12632
12632
  expected = unshift(expected);
12633
12633
  const globalChildren = expectConfig().toMatchAriaSnapshot?.children;
@@ -12668,12 +12668,12 @@ async function toMatchAriaSnapshot(receiver, expectedParam, options = {}) {
12668
12668
  if (errorMessage)
12669
12669
  return { pass: this.isNot, message, name: "toMatchAriaSnapshot", expected };
12670
12670
  if (!this.isNot) {
12671
- if (updateSnapshots === "all" || updateSnapshots === "changed" && pass === this.isNot || generateMissingBaseline) {
12671
+ if (generateBaseline || updateSnapshots === "changed" && pass === this.isNot) {
12672
12672
  if (expectedPath) {
12673
12673
  await import_fs2.default.promises.mkdir(import_path2.default.dirname(expectedPath), { recursive: true });
12674
12674
  await import_fs2.default.promises.writeFile(expectedPath, typedReceived.regex, "utf8");
12675
12675
  const relativePath = import_path2.default.relative(process.cwd(), expectedPath);
12676
- if (updateSnapshots === "missing") {
12676
+ if (isMissingBaseline) {
12677
12677
  const message3 = `A snapshot doesn't exist at ${relativePath}, writing actual.`;
12678
12678
  return { pass: true, message: () => "", name: "toMatchAriaSnapshot", softError: new Error(message3), shouldNotRetryTest: true };
12679
12679
  }
@@ -12684,7 +12684,7 @@ async function toMatchAriaSnapshot(receiver, expectedParam, options = {}) {
12684
12684
  const suggestedRebaseline = `\`
12685
12685
  ${escapeTemplateString(indent2(typedReceived.regex, "{indent} "))}
12686
12686
  {indent}\``;
12687
- if (updateSnapshots === "missing") {
12687
+ if (isMissingBaseline) {
12688
12688
  const message2 = "A snapshot is not provided, generating new baseline.";
12689
12689
  return { pass: true, message: () => "", name: "toMatchAriaSnapshot", suggestedRebaseline, softError: new Error(message2), shouldNotRetryTest: true };
12690
12690
  }
@@ -12818,9 +12818,11 @@ function createExpect(info) {
12818
12818
  newInfo.isSoft = configuration.soft;
12819
12819
  return createExpect(newInfo);
12820
12820
  };
12821
- expectFn.soft = (actual, messageOrOptions) => {
12822
- return createMatchers(actual, { ...info, isSoft: true }, messageOrOptions);
12823
- };
12821
+ Object.defineProperty(expectFn, "soft", {
12822
+ configurable: true,
12823
+ enumerable: true,
12824
+ get: () => info.isSoft ? expectFn : createExpect({ ...info, isSoft: true })
12825
+ });
12824
12826
  expectFn.poll = (actual, messageOrOptions) => {
12825
12827
  const poll = isString4(messageOrOptions) ? {} : messageOrOptions || {};
12826
12828
  return createMatchers(actual, { ...info, poll: { timeout: poll.timeout, intervals: poll.intervals } }, messageOrOptions);
@@ -1,5 +1,5 @@
1
1
  # packages/playwright/lib/matchers/expect.js
2
- # total: 513.7 KB
2
+ # total: 513.8 KB
3
3
 
4
4
  ## Inlined (64)
5
5
  7.9 KB node_modules/@babel/code-frame/lib/index.js
@@ -56,14 +56,14 @@
56
56
  7.6 KB node_modules/stack-utils/index.js
57
57
  0.4 KB node_modules/stack-utils/node_modules/escape-string-regexp/index.js
58
58
  6.8 KB node_modules/to-regex-range/index.js
59
- 10.3 KB packages/playwright/src/matchers/expect.ts
59
+ 10.4 KB packages/playwright/src/matchers/expect.ts
60
60
  45.2 KB packages/playwright/src/matchers/expectLibrary.ts
61
61
  3.3 KB packages/playwright/src/matchers/matcherHint.ts
62
62
  16.0 KB packages/playwright/src/matchers/matchers.ts
63
63
  1.3 KB packages/playwright/src/matchers/toBeTruthy.ts
64
64
  2.1 KB packages/playwright/src/matchers/toEqual.ts
65
65
  2.4 KB packages/playwright/src/matchers/toHaveURL.ts
66
- 5.6 KB packages/playwright/src/matchers/toMatchAriaSnapshot.ts
66
+ 5.7 KB packages/playwright/src/matchers/toMatchAriaSnapshot.ts
67
67
  15.1 KB packages/playwright/src/matchers/toMatchSnapshot.ts
68
68
  3.0 KB packages/playwright/src/matchers/toMatchText.ts
69
69
 
@@ -89,6 +89,7 @@ class TestContext {
89
89
  async _enqueue(fn) {
90
90
  const next = this._testOpQueue.then(fn);
91
91
  this._testOpQueue = next.then(() => {
92
+ }, () => {
92
93
  });
93
94
  return await next;
94
95
  }
@@ -199,7 +200,8 @@ class TestContext {
199
200
  releaseStdio();
200
201
  }
201
202
  };
202
- const abortPromise = signal ? signalToPromise(signal).promise.then(() => "interrupted") : new Promise(() => {
203
+ const abort = signal ? signalToPromise(signal) : void 0;
204
+ const abortPromise = abort ? abort.promise.then(() => "interrupted") : new Promise(() => {
203
205
  });
204
206
  try {
205
207
  const reporter = new MCPListReporter({ configDir, screen, includeTestId: true });
@@ -225,6 +227,8 @@ class TestContext {
225
227
  testRunnerAndScreen.output.push(String(e));
226
228
  await cleanup();
227
229
  return { output: testRunnerAndScreen.output.join("\n"), status };
230
+ } finally {
231
+ abort?.dispose();
228
232
  }
229
233
  await cleanup();
230
234
  return { output: testRunnerAndScreen.output.join("\n"), status };
package/lib/program.js CHANGED
@@ -46,8 +46,11 @@ function addTestCommand(program2) {
46
46
  return command;
47
47
  });
48
48
  command.action(async (args, opts) => {
49
+ const dashDashIndex = process.argv.indexOf("--");
50
+ const postDashCount = dashDashIndex >= 0 ? process.argv.length - 1 - dashDashIndex : 0;
51
+ const testFilters = args.slice(0, args.length - postDashCount);
49
52
  try {
50
- await (0, import_testActions.runTests)(args, opts);
53
+ await (0, import_testActions.runTests)(testFilters, opts);
51
54
  } catch (e) {
52
55
  console.error(e);
53
56
  gracefullyProcessExitDoNotHang(1);
@@ -144,7 +147,7 @@ function addInitAgentsCommand(program2) {
144
147
  const command = program2.command("init-agents");
145
148
  command.description("Initialize repository agents");
146
149
  const option = command.createOption("--loop <loop>", "Agentic loop provider");
147
- option.choices(["claude", "copilot", "opencode", "vscode", "vscode-legacy"]);
150
+ option.choices(["claude", "codex", "copilot", "opencode", "vscode", "vscode-legacy"]);
148
151
  command.addOption(option);
149
152
  command.option("-c, --config <file>", `Configuration file to find a project to use for seed test`);
150
153
  command.option("--project <project>", "Project to use for seed test");
@@ -157,6 +160,8 @@ function addInitAgentsCommand(program2) {
157
160
  await import_generateAgents.VSCodeGenerator.init(loadedConfig, opts.project);
158
161
  } else if (opts.loop === "claude") {
159
162
  await import_generateAgents.ClaudeGenerator.init(loadedConfig, opts.project, opts.prompts);
163
+ } else if (opts.loop === "codex") {
164
+ await import_generateAgents.CodexGenerator.init(loadedConfig, opts.project, opts.prompts);
160
165
  } else {
161
166
  await import_generateAgents.CopilotGenerator.init(loadedConfig, opts.project, opts.prompts);
162
167
  return;
@@ -174,10 +179,11 @@ const testOptions = [
174
179
  ["--fully-parallel", { description: `Run all tests in parallel (default: false)` }],
175
180
  ["--global-timeout <timeout>", { description: `Maximum time this test suite can run in milliseconds (default: unlimited)` }],
176
181
  ["-g, --grep <grep>", { description: `Only run tests matching this regular expression (default: ".*")` }],
177
- ["--grep-invert <grep>", { description: `Only run tests that do not match this regular expression` }],
182
+ ["-G, --grep-invert <grep>", { description: `Only run tests that do not match this regular expression` }],
178
183
  ["--headed", { description: `Run tests in headed browsers (default: headless)` }],
179
184
  ["--ignore-snapshots", { description: `Ignore screenshot and snapshot expectations` }],
180
185
  ["--last-failed", { description: `Only re-run the failures` }],
186
+ ["--last-failed-file <file>", { description: `Override the default path for the last-run JSON file used with --last-failed (default: <outputDir>/.last-run.json). Same as PLAYWRIGHT_LAST_RUN_OUTPUT_FILE environment variable.` }],
181
187
  ["--list", { description: `Collect all the tests and report them, but do not run` }],
182
188
  ["--max-failures <N>", { description: `Stop after the first N failures` }],
183
189
  ["--no-deps", { description: `Do not run project dependencies` }],