playwright 1.57.0-alpha-2025-10-19 → 1.57.0-alpha-2025-10-21

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.
@@ -69,6 +69,11 @@ class FullConfigInternal {
69
69
  this.globalSetups = (Array.isArray(userConfig.globalSetup) ? userConfig.globalSetup : [userConfig.globalSetup]).map((s) => resolveScript(s, configDir)).filter((script) => script !== void 0);
70
70
  this.globalTeardowns = (Array.isArray(userConfig.globalTeardown) ? userConfig.globalTeardown : [userConfig.globalTeardown]).map((s) => resolveScript(s, configDir)).filter((script) => script !== void 0);
71
71
  userConfig.metadata = userConfig.metadata || {};
72
+ const globalTags = Array.isArray(userConfig.tag) ? userConfig.tag : userConfig.tag ? [userConfig.tag] : [];
73
+ for (const tag of globalTags) {
74
+ if (tag[0] !== "@")
75
+ throw new Error(`Tag must start with "@" symbol, got "${tag}" instead.`);
76
+ }
72
77
  this.config = {
73
78
  configFile: resolvedConfigFile,
74
79
  rootDir: pathResolve(configDir, userConfig.testDir) || configDir,
@@ -91,6 +96,7 @@ class FullConfigInternal {
91
96
  quiet: takeFirst(configCLIOverrides.quiet, userConfig.quiet, false),
92
97
  projects: [],
93
98
  shard: takeFirst(configCLIOverrides.shard, userConfig.shard, null),
99
+ tags: globalTags,
94
100
  updateSnapshots: takeFirst(configCLIOverrides.updateSnapshots, userConfig.updateSnapshots, "missing"),
95
101
  updateSourceMethod: takeFirst(configCLIOverrides.updateSourceMethod, userConfig.updateSourceMethod, "patch"),
96
102
  version: require("../../package.json").version,
@@ -42,12 +42,13 @@ var import_transform = require("../transform/transform");
42
42
  var import_util2 = require("../util");
43
43
  const defaultTimeout = 3e4;
44
44
  const cachedFileSuites = /* @__PURE__ */ new Map();
45
- async function loadTestFile(file, rootDir, testErrors) {
45
+ async function loadTestFile(file, config, testErrors) {
46
46
  if (cachedFileSuites.has(file))
47
47
  return cachedFileSuites.get(file);
48
- const suite = new import_test.Suite(import_path.default.relative(rootDir, file) || import_path.default.basename(file), "file");
48
+ const suite = new import_test.Suite(import_path.default.relative(config.config.rootDir, file) || import_path.default.basename(file), "file");
49
49
  suite._requireFile = file;
50
50
  suite.location = { file, line: 0, column: 0 };
51
+ suite._tags = [...config.config.tags];
51
52
  (0, import_globals.setCurrentlyLoadingFileSuite)(suite);
52
53
  if (!(0, import_globals.isWorkerProcess)()) {
53
54
  (0, import_compilationCache.startCollectingFileDeps)();
@@ -445,6 +445,7 @@ const baseFullConfig = {
445
445
  rootDir: "",
446
446
  quiet: false,
447
447
  shard: null,
448
+ tags: [],
448
449
  updateSnapshots: "missing",
449
450
  updateSourceMethod: "patch",
450
451
  version: "",
@@ -42,7 +42,7 @@ class LoaderMain extends import_process.ProcessRunner {
42
42
  async loadTestFile(params) {
43
43
  const testErrors = [];
44
44
  const config = await this._config();
45
- const fileSuite = await (0, import_testLoader.loadTestFile)(params.file, config.config.rootDir, testErrors);
45
+ const fileSuite = await (0, import_testLoader.loadTestFile)(params.file, config, testErrors);
46
46
  this._poolBuilder.buildPools(fileSuite);
47
47
  return { fileSuite: fileSuite._deepSerialize(), testErrors };
48
48
  }
@@ -173,8 +173,10 @@ class JUnitReporter {
173
173
  const systemOut = [];
174
174
  const systemErr = [];
175
175
  for (const result of test.results) {
176
- systemOut.push(...result.stdout.map((item) => item.toString()));
177
- systemErr.push(...result.stderr.map((item) => item.toString()));
176
+ for (const item of result.stdout)
177
+ systemOut.push(item.toString());
178
+ for (const item of result.stderr)
179
+ systemErr.push(item.toString());
178
180
  for (const attachment of result.attachments) {
179
181
  if (!attachment.path)
180
182
  continue;
@@ -72,13 +72,15 @@ async function createMergedReport(config, dir, reporterDescriptions, rootDirOver
72
72
  }
73
73
  };
74
74
  await dispatchEvents(eventData.prologue);
75
- for (const { reportFile, eventPatchers, metadata } of eventData.reports) {
75
+ for (const { reportFile, eventPatchers, metadata, tags } of eventData.reports) {
76
76
  const reportJsonl = await import_fs.default.promises.readFile(reportFile);
77
77
  const events = parseTestEvents(reportJsonl);
78
78
  new import_stringInternPool.JsonStringInternalizer(stringPool).traverse(events);
79
79
  eventPatchers.patchers.push(new AttachmentPathPatcher(dir));
80
80
  if (metadata.name)
81
81
  eventPatchers.patchers.push(new GlobalErrorPatcher(metadata.name));
82
+ if (tags.length)
83
+ eventPatchers.patchers.push(new GlobalErrorPatcher(tags.join(" ")));
82
84
  eventPatchers.patchEvents(events);
83
85
  await dispatchEvents(events);
84
86
  }
@@ -178,18 +180,22 @@ async function mergeEvents(dir, shardReportFiles, stringPool, printStatus, rootD
178
180
  if (rootDirOverride)
179
181
  eventPatchers.patchers.push(new PathSeparatorPatcher(metadata.pathSeparator));
180
182
  eventPatchers.patchEvents(parsedEvents);
183
+ let tags = [];
181
184
  for (const event of parsedEvents) {
182
- if (event.method === "onConfigure")
185
+ if (event.method === "onConfigure") {
183
186
  configureEvents.push(event);
184
- else if (event.method === "onProject")
187
+ tags = event.params.config.tags || [];
188
+ } else if (event.method === "onProject") {
185
189
  projectEvents.push(event);
186
- else if (event.method === "onEnd")
190
+ } else if (event.method === "onEnd") {
187
191
  endEvents.push(event);
192
+ }
188
193
  }
189
194
  reports.push({
190
195
  eventPatchers,
191
196
  reportFile: localPath,
192
- metadata
197
+ metadata,
198
+ tags
193
199
  });
194
200
  }
195
201
  return {
@@ -154,7 +154,8 @@ class TeleReporterEmitter {
154
154
  version: config.version,
155
155
  workers: config.workers,
156
156
  globalSetup: config.globalSetup,
157
- globalTeardown: config.globalTeardown
157
+ globalTeardown: config.globalTeardown,
158
+ tags: config.tags
158
159
  };
159
160
  }
160
161
  _serializeProject(suite) {
@@ -48,7 +48,7 @@ class InProcessLoaderHost {
48
48
  return true;
49
49
  }
50
50
  async loadTestFile(file, testErrors) {
51
- const result = await (0, import_testLoader.loadTestFile)(file, this._config.config.rootDir, testErrors);
51
+ const result = await (0, import_testLoader.loadTestFile)(file, this._config, testErrors);
52
52
  this._poolBuilder.buildPools(result, testErrors);
53
53
  return result;
54
54
  }
@@ -125,6 +125,8 @@ function computeCommandHash(config) {
125
125
  command.cliGrepInvert = config.cliGrepInvert;
126
126
  if (config.cliOnlyChanged)
127
127
  command.cliOnlyChanged = config.cliOnlyChanged;
128
+ if (config.config.tags.length)
129
+ command.tags = config.config.tags.join(" ");
128
130
  if (Object.keys(command).length)
129
131
  parts.push((0, import_utils.calculateSha1)(JSON.stringify(command)).substring(0, 7));
130
132
  return parts.join("-");
@@ -179,7 +179,7 @@ class WorkerMain extends import_process.ProcessRunner {
179
179
  let fatalUnknownTestIds;
180
180
  try {
181
181
  await this._loadIfNeeded();
182
- const fileSuite = await (0, import_testLoader.loadTestFile)(runPayload.file, this._config.config.rootDir);
182
+ const fileSuite = await (0, import_testLoader.loadTestFile)(runPayload.file, this._config);
183
183
  const suite = (0, import_suiteUtils.bindFileSuiteToProject)(this._project, fileSuite);
184
184
  if (this._params.repeatEachIndex)
185
185
  (0, import_suiteUtils.applyRepeatEachIndex)(this._project, suite, this._params.repeatEachIndex);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playwright",
3
- "version": "1.57.0-alpha-2025-10-19",
3
+ "version": "1.57.0-alpha-2025-10-21",
4
4
  "description": "A high-level API to automate web browsers",
5
5
  "repository": {
6
6
  "type": "git",
@@ -64,7 +64,7 @@
64
64
  },
65
65
  "license": "Apache-2.0",
66
66
  "dependencies": {
67
- "playwright-core": "1.57.0-alpha-2025-10-19"
67
+ "playwright-core": "1.57.0-alpha-2025-10-21"
68
68
  },
69
69
  "optionalDependencies": {
70
70
  "fsevents": "2.3.2"
package/types/test.d.ts CHANGED
@@ -1758,6 +1758,26 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
1758
1758
  */
1759
1759
  snapshotPathTemplate?: string;
1760
1760
 
1761
+ /**
1762
+ * Tag or tags prepended to each test in the report. Useful for tagging your test run to differentiate between
1763
+ * [CI environments](https://playwright.dev/docs/test-sharding#merging-reports-from-multiple-environments).
1764
+ *
1765
+ * Note that each tag must start with `@` symbol. Learn more about [tagging](https://playwright.dev/docs/test-annotations#tag-tests).
1766
+ *
1767
+ * **Usage**
1768
+ *
1769
+ * ```js
1770
+ * // playwright.config.ts
1771
+ * import { defineConfig } from '@playwright/test';
1772
+ *
1773
+ * export default defineConfig({
1774
+ * tag: process.env.CI_ENVIRONMENT_NAME, // for example "@APIv2"
1775
+ * });
1776
+ * ```
1777
+ *
1778
+ */
1779
+ tag?: string|Array<string>;
1780
+
1761
1781
  /**
1762
1782
  * Directory that will be recursively scanned for test files. Defaults to the directory of the configuration file.
1763
1783
  *
@@ -2035,6 +2055,11 @@ export interface FullConfig<TestArgs = {}, WorkerArgs = {}> {
2035
2055
  current: number;
2036
2056
  };
2037
2057
 
2058
+ /**
2059
+ * Resolved global tags. See [testConfig.tag](https://playwright.dev/docs/api/class-testconfig#test-config-tag).
2060
+ */
2061
+ tags: Array<string>;
2062
+
2038
2063
  /**
2039
2064
  * See [testConfig.updateSnapshots](https://playwright.dev/docs/api/class-testconfig#test-config-update-snapshots).
2040
2065
  */
@@ -6180,7 +6205,7 @@ export interface TestType<TestArgs extends {}, WorkerArgs extends {}> {
6180
6205
  * const name = this.constructor.name + '.' + (context.name as string);
6181
6206
  * return test.step(name, async () => {
6182
6207
  * return await target.call(this, ...args);
6183
- * });
6208
+ * }, { box: true });
6184
6209
  * };
6185
6210
  * }
6186
6211
  *
@@ -6339,7 +6364,7 @@ export interface TestType<TestArgs extends {}, WorkerArgs extends {}> {
6339
6364
  * const name = this.constructor.name + '.' + (context.name as string);
6340
6365
  * return test.step(name, async () => {
6341
6366
  * return await target.call(this, ...args);
6342
- * });
6367
+ * }, { box: true });
6343
6368
  * };
6344
6369
  * }
6345
6370
  *
@@ -127,6 +127,11 @@ export interface FullResult {
127
127
  * `false`. This way, Playwright will use one of the standard terminal reporters in addition to your custom reporter
128
128
  * to enhance user experience.
129
129
  *
130
+ * **Reporter errors**
131
+ *
132
+ * Playwright will swallow any errors thrown in your custom reporter methods. If you need to detect or fail on
133
+ * reporter errors, you must wrap and handle them yourself.
134
+ *
130
135
  * **Merged report API notes**
131
136
  *
132
137
  * When merging multiple [`blob`](https://playwright.dev/docs/test-reporters#blob-reporter) reports via