@gooddata/sdk-e2e-utils 11.61.0-alpha.0 → 11.61.0-alpha.2

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 (55) hide show
  1. package/esm/create-test/feature-hub/mock.d.ts +2 -2
  2. package/esm/create-test/feature-hub/mock.d.ts.map +1 -1
  3. package/esm/create-test/goodmock/snapshot/sanitize-organization.d.ts +12 -0
  4. package/esm/create-test/goodmock/snapshot/sanitize-organization.d.ts.map +1 -0
  5. package/esm/create-test/goodmock/snapshot/sanitize-organization.js +28 -0
  6. package/esm/create-test/goodmock/snapshot/snapshot-and-save-recording.d.ts +6 -0
  7. package/esm/create-test/goodmock/snapshot/snapshot-and-save-recording.d.ts.map +1 -1
  8. package/esm/create-test/goodmock/snapshot/snapshot-and-save-recording.js +4 -2
  9. package/esm/create-test/playwright/factory.d.ts +8 -0
  10. package/esm/create-test/playwright/factory.d.ts.map +1 -0
  11. package/esm/create-test/playwright/factory.js +143 -0
  12. package/esm/create-test/playwright/fixtures.d.ts +2 -2
  13. package/esm/create-test/playwright/fixtures.d.ts.map +1 -1
  14. package/esm/create-test/playwright/playwright-internals.d.ts +38 -0
  15. package/esm/create-test/playwright/playwright-internals.d.ts.map +1 -0
  16. package/esm/create-test/playwright/playwright-internals.js +60 -0
  17. package/esm/create-test/playwright/playwright-types.d.ts +12 -20
  18. package/esm/create-test/playwright/playwright-types.d.ts.map +1 -1
  19. package/esm/create-test/playwright/test-conditions.d.ts +25 -0
  20. package/esm/create-test/playwright/test-conditions.d.ts.map +1 -0
  21. package/esm/create-test/playwright/test-conditions.js +76 -0
  22. package/esm/create-test/playwright/to-playwright-details.d.ts +5 -0
  23. package/esm/create-test/playwright/to-playwright-details.d.ts.map +1 -0
  24. package/esm/create-test/playwright/to-playwright-details.js +7 -0
  25. package/esm/create-test/playwright/types.d.ts +133 -2
  26. package/esm/create-test/playwright/types.d.ts.map +1 -1
  27. package/esm/create-test/playwright/with-describe-details.d.ts +10 -4
  28. package/esm/create-test/playwright/with-describe-details.d.ts.map +1 -1
  29. package/esm/create-test/playwright/with-describe-details.js +47 -14
  30. package/esm/index.d.ts +3 -7
  31. package/esm/index.d.ts.map +1 -1
  32. package/esm/index.js +2 -1
  33. package/esm/sdk-e2e-utils.d.ts +144 -70
  34. package/package.json +3 -3
  35. package/esm/create-test/playwright/call-original-fn.d.ts +0 -17
  36. package/esm/create-test/playwright/call-original-fn.d.ts.map +0 -1
  37. package/esm/create-test/playwright/call-original-fn.js +0 -25
  38. package/esm/create-test/playwright/describe/factory.d.ts +0 -27
  39. package/esm/create-test/playwright/describe/factory.d.ts.map +0 -1
  40. package/esm/create-test/playwright/describe/factory.js +0 -18
  41. package/esm/create-test/playwright/test/factory.d.ts +0 -7
  42. package/esm/create-test/playwright/test/factory.d.ts.map +0 -1
  43. package/esm/create-test/playwright/test/factory.js +0 -42
  44. package/esm/create-test/playwright/test/types.d.ts +0 -27
  45. package/esm/create-test/playwright/test/types.d.ts.map +0 -1
  46. package/esm/create-test/playwright/test/types.js +0 -2
  47. package/esm/create-test/playwright/top-level-describe/build-suite-callback.d.ts +0 -9
  48. package/esm/create-test/playwright/top-level-describe/build-suite-callback.d.ts.map +0 -1
  49. package/esm/create-test/playwright/top-level-describe/build-suite-callback.js +0 -17
  50. package/esm/create-test/playwright/top-level-describe/factory.d.ts +0 -8
  51. package/esm/create-test/playwright/top-level-describe/factory.d.ts.map +0 -1
  52. package/esm/create-test/playwright/top-level-describe/factory.js +0 -12
  53. package/esm/create-test/playwright/top-level-describe/types.d.ts +0 -17
  54. package/esm/create-test/playwright/top-level-describe/types.d.ts.map +0 -1
  55. package/esm/create-test/playwright/top-level-describe/types.js +0 -2
@@ -1,5 +1,5 @@
1
- import type { Test } from "../playwright/playwright-types.js";
1
+ import type { PlaywrightTestInstance } from "../playwright/playwright-types.js";
2
2
  import type { IFeatureHubEnvironment } from "./types.js";
3
3
  /** FeatureHub mock — each test gets a fresh page, so route dies with it. */
4
- export declare function registerFeatureHubMock(testInstance: Test, featureHubResponse: IFeatureHubEnvironment[]): void;
4
+ export declare function registerFeatureHubMock(testInstance: PlaywrightTestInstance, featureHubResponse: IFeatureHubEnvironment[]): void;
5
5
  //# sourceMappingURL=mock.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../../src/create-test/feature-hub/mock.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mCAAmC,CAAC;AAE9D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,4EAA4E;AAC5E,wBAAgB,sBAAsB,CAClC,YAAY,EAAE,IAAI,EAClB,kBAAkB,EAAE,sBAAsB,EAAE,GAC7C,IAAI,CAYN"}
1
+ {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../../src/create-test/feature-hub/mock.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAEhF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,4EAA4E;AAC5E,wBAAgB,sBAAsB,CAClC,YAAY,EAAE,sBAAsB,EACpC,kBAAkB,EAAE,sBAAsB,EAAE,GAC7C,IAAI,CAYN"}
@@ -0,0 +1,12 @@
1
+ import { type IGoodmockMapping } from "../types.js";
2
+ export declare const DEFAULT_TEMPLATE_ORGANIZATION = "automation";
3
+ /**
4
+ * Replace every ephemeral organization name (`ephemeral-{templateOrganization}-{hex}`) with the
5
+ * bare template organization name, so re-recording against a freshly created org does not churn
6
+ * the organization id, name, or `/entities/admin/organizations/...` paths in the saved mappings.
7
+ *
8
+ * The hex suffix is exactly 8 lowercase hex characters; the lookahead makes sure a longer hex run
9
+ * is not partially consumed.
10
+ */
11
+ export declare function sanitizeOrganization(mappings: IGoodmockMapping[], templateOrganization?: string): IGoodmockMapping[];
12
+ //# sourceMappingURL=sanitize-organization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize-organization.d.ts","sourceRoot":"","sources":["../../../../src/create-test/goodmock/snapshot/sanitize-organization.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,eAAO,MAAM,6BAA6B,eAAe,CAAC;AAM1D;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAChC,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,oBAAoB,GAAE,MAAsC,GAC7D,gBAAgB,EAAE,CAmBpB"}
@@ -0,0 +1,28 @@
1
+ // (C) 2026 GoodData Corporation
2
+ export const DEFAULT_TEMPLATE_ORGANIZATION = "automation";
3
+ function escapeRegExp(value) {
4
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5
+ }
6
+ /**
7
+ * Replace every ephemeral organization name (`ephemeral-{templateOrganization}-{hex}`) with the
8
+ * bare template organization name, so re-recording against a freshly created org does not churn
9
+ * the organization id, name, or `/entities/admin/organizations/...` paths in the saved mappings.
10
+ *
11
+ * The hex suffix is exactly 8 lowercase hex characters; the lookahead makes sure a longer hex run
12
+ * is not partially consumed.
13
+ */
14
+ export function sanitizeOrganization(mappings, templateOrganization = DEFAULT_TEMPLATE_ORGANIZATION) {
15
+ const template = escapeRegExp(templateOrganization);
16
+ const ephemeralOrganizationPattern = new RegExp(`ephemeral-${template}-[0-9a-f]{8}(?![0-9a-f])`, "g");
17
+ const dataString = JSON.stringify({ mappings });
18
+ const matches = dataString.match(ephemeralOrganizationPattern);
19
+ if (!matches) {
20
+ return mappings;
21
+ }
22
+ const sanitizedDataString = dataString.replace(ephemeralOrganizationPattern, templateOrganization);
23
+ const sanitizedData = JSON.parse(sanitizedDataString);
24
+ const distinctOrganizations = [...new Set(matches)].join(", ");
25
+ // oxlint-disable-next-line eslint-js/no-console
26
+ console.log(`Sanitized organization in mappings (${distinctOrganizations} -> ${templateOrganization})`);
27
+ return sanitizedData.mappings;
28
+ }
@@ -5,6 +5,12 @@ import { type IGoodmockMapping, type ILeakPattern, type ISecretMapping, type IWo
5
5
  export interface ISnapshotAndSaveRecordingOptions {
6
6
  /** Source/target workspace ID rewrite(s) applied before mappings are saved. */
7
7
  workspaceIdMappings?: IWorkspaceIdMapping | IWorkspaceIdMapping[];
8
+ /**
9
+ * Template organization name. Every ephemeral organization name in the saved mappings
10
+ * (`ephemeral-{templateOrganization}-{hex}`) is replaced with this bare name so re-recording
11
+ * against a fresh ephemeral org does not churn the recordings. Defaults to "automation".
12
+ */
13
+ templateOrganization?: string;
8
14
  /**
9
15
  * Real backend URL (e.g. "https://example.gooddata.com"). When provided together with
10
16
  * baseUrl, all occurrences are replaced in the saved mappings so that recorded responses
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot-and-save-recording.d.ts","sourceRoot":"","sources":["../../../../src/create-test/goodmock/snapshot/snapshot-and-save-recording.ts"],"names":[],"mappings":"AAKA,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EAC3B,MAAM,aAAa,CAAC;AASrB;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAC7C,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,mBAAmB,GAAG,mBAAmB,EAAE,CAAC;IAClE;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gGAAgG;IAChG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,cAAc,EAAE,CAAC;IAClC;;;;OAIG;IACH,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC;IAC9B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,CAAC;CAC3E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAC1C,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,MAAM,EACvB,OAAO,GAAE,gCAAqC,GAC/C,OAAO,CAAC,IAAI,CAAC,CAoDf"}
1
+ {"version":3,"file":"snapshot-and-save-recording.d.ts","sourceRoot":"","sources":["../../../../src/create-test/goodmock/snapshot/snapshot-and-save-recording.ts"],"names":[],"mappings":"AAKA,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EAC3B,MAAM,aAAa,CAAC;AAUrB;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAC7C,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,mBAAmB,GAAG,mBAAmB,EAAE,CAAC;IAClE;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gGAAgG;IAChG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,cAAc,EAAE,CAAC;IAClC;;;;OAIG;IACH,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC;IAC9B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,CAAC;CAC3E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAC1C,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,MAAM,EACvB,OAAO,GAAE,gCAAqC,GAC/C,OAAO,CAAC,IAAI,CAAC,CA6Df"}
@@ -5,6 +5,7 @@ import { assertNoLeakPatterns } from "./assert-no-leak-patterns.js";
5
5
  import { assertNoSecretLeaks } from "./assert-no-secret-leaks.js";
6
6
  import { replaceSecrets } from "./replace-secrets.js";
7
7
  import { sanitizeCredentials } from "./sanitize-credentials.js";
8
+ import { DEFAULT_TEMPLATE_ORGANIZATION, sanitizeOrganization } from "./sanitize-organization.js";
8
9
  import { sanitizeWorkspaceIds } from "./sanitize-workspace-ids.js";
9
10
  import { snapshotParams } from "./snapshot-param.js";
10
11
  /**
@@ -21,7 +22,7 @@ import { snapshotParams } from "./snapshot-param.js";
21
22
  * @param options - See {@link ISnapshotAndSaveRecordingOptions}.
22
23
  */
23
24
  export async function snapshotAndSaveRecording(host, mappingFilePath, options = {}) {
24
- const { workspaceIdMappings, backendHost, baseUrl, secretMappings, leakPatterns, sanitizeMappings } = options;
25
+ const { workspaceIdMappings, templateOrganization = DEFAULT_TEMPLATE_ORGANIZATION, backendHost, baseUrl, secretMappings, leakPatterns, sanitizeMappings, } = options;
25
26
  // Snapshot everything with scenarios enabled. goodmock only emits a scenario
26
27
  // chain when a given request produced *different* responses across the
27
28
  // recording; identical repeats collapse to a single mapping. This captures
@@ -49,7 +50,8 @@ export async function snapshotAndSaveRecording(host, mappingFilePath, options =
49
50
  // Consumer-supplied redaction for secrets this module has no knowledge of (e.g. an LLM
50
51
  // provider's API key echoed back on a settings entity).
51
52
  const sanitizedByCaller = sanitizeMappings ? sanitizeMappings(mappings) : mappings;
52
- const sanitizedMappings = sanitizeWorkspaceIds(sanitizedByCaller, workspaceIdMappings);
53
+ const sanitizedWorkspaceIds = sanitizeWorkspaceIds(sanitizedByCaller, workspaceIdMappings);
54
+ const sanitizedMappings = sanitizeOrganization(sanitizedWorkspaceIds, templateOrganization);
53
55
  let output = JSON.stringify({ mappings: sanitizedMappings }, null, 4) + "\n";
54
56
  if (backendHost && baseUrl) {
55
57
  output = output.replaceAll(backendHost, baseUrl);
@@ -0,0 +1,8 @@
1
+ import type { PlaywrightBaseTestArgs, PlaywrightBaseWorkerArgs } from "./playwright-types.js";
2
+ import type { ICreateTestOptions, Test, TestConditionsValueConstraint } from "./types.js";
3
+ /**
4
+ * @internal
5
+ * @param options -
6
+ */
7
+ export declare function createTest<TestConditionsValue extends TestConditionsValueConstraint = never, T extends {} = {}, W extends {} = {}>(options: ICreateTestOptions<TestConditionsValue, T, W>): Test<TestConditionsValue, PlaywrightBaseTestArgs & T, PlaywrightBaseWorkerArgs & W>;
8
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/factory.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACR,sBAAsB,EACtB,wBAAwB,EAE3B,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,KAAK,EAER,kBAAkB,EAElB,IAAI,EACJ,6BAA6B,EAGhC,MAAM,YAAY,CAAC;AAGpB;;;GAGG;AACH,wBAAgB,UAAU,CACtB,mBAAmB,SAAS,6BAA6B,GAAG,KAAK,EACjE,CAAC,SAAS,EAAE,GAAG,EAAE,EACjB,CAAC,SAAS,EAAE,GAAG,EAAE,EAEjB,OAAO,EAAE,kBAAkB,CAAC,mBAAmB,EAAE,CAAC,EAAE,CAAC,CAAC,GACvD,IAAI,CAAC,mBAAmB,EAAE,sBAAsB,GAAG,CAAC,EAAE,wBAAwB,GAAG,CAAC,CAAC,CAsMrF"}
@@ -0,0 +1,143 @@
1
+ // (C) 2026 GoodData Corporation
2
+ // oxlint-disable typescript-eslint/no-empty-object-type
3
+ import { basename } from "path";
4
+ import { test } from "@playwright/test";
5
+ import { registerFeatureHubMock } from "../feature-hub/mock.js";
6
+ import { registerGoodmockHooks } from "../goodmock/playwright.js";
7
+ import { getCallerLocation, getPlaywrightInternals, } from "./playwright-internals.js";
8
+ import { assertRequiredConditions, assertValidConditions, combineConditions, shouldRunTest, } from "./test-conditions.js";
9
+ import { toPlaywrightDetails } from "./to-playwright-details.js";
10
+ import { withDescribeDetails, withTestDetails } from "./with-describe-details.js";
11
+ /**
12
+ * @internal
13
+ * @param options -
14
+ */
15
+ export function createTest(options) {
16
+ // Tracks whether we're inside the outermost describe of a spec file during synchronous registration.
17
+ let insideRootDescribe = false;
18
+ // Stacks track workspace settings / additionalWindowProperties hierarchy during
19
+ // synchronous describe registration. Each level merges on top of the parent's.
20
+ // The beforeEach at each level captures the fully-merged snapshot, so the
21
+ // innermost beforeEach (which runs last) wins.
22
+ const settingsStack = [];
23
+ const awpStack = [];
24
+ // Combined test conditions and titles of the enclosing describes, during synchronous registration.
25
+ // Describes only feed these; each test decides for itself whether it runs (a describe whose own
26
+ // conditions are false may still contain tests whose combined conditions are true).
27
+ const { testConditions } = options;
28
+ const conditionsStack = [];
29
+ const titleStack = [];
30
+ const withDescribeScope = (title, details, body) => {
31
+ conditionsStack.push(combineConditions(testConditions, conditionsStack[conditionsStack.length - 1], details.conditions));
32
+ titleStack.push(title);
33
+ try {
34
+ body();
35
+ }
36
+ finally {
37
+ titleStack.pop();
38
+ conditionsStack.pop();
39
+ }
40
+ };
41
+ // Same type `test.extend<T, W>()` returns. Without fixtures, T and W (inferred from `options.fixtures`)
42
+ // add nothing, so the base `test` already is that type.
43
+ const testInstance = options.fixtures
44
+ ? test.extend(options.fixtures)
45
+ : test;
46
+ const internals = getPlaywrightInternals(testInstance);
47
+ // specName -> spec file it was derived from. Playwright loads each file once per process, so the
48
+ // same file again means a second outermost describe in it, and another file means a duplicate specName.
49
+ const specNameFiles = new Map();
50
+ const makeDescribe = (type, name) => {
51
+ const describeFunction = (title, details, callback) => {
52
+ const location = getCallerLocation(describeFunction);
53
+ assertValidConditions(details.conditions, `${name}("${title}")`);
54
+ const pwDetails = toPlaywrightDetails(details);
55
+ if (insideRootDescribe) {
56
+ internals.describe(type, location, title, pwDetails, () => withDescribeScope(title, details, () => withDescribeDetails(testInstance, settingsStack, awpStack, details, callback)));
57
+ return;
58
+ }
59
+ // Outermost describe of the spec file: it also sets up the spec.
60
+ // "some-file-name.spec.ts" -> "some-file-name"
61
+ const specName = basename(location.file).replace(/\.(spec|test)\.[cm]?[jt]sx?$/, "");
62
+ const existingFile = specNameFiles.get(specName);
63
+ if (existingFile === location.file) {
64
+ throw new Error(`${name}("${title}"): only one outermost test.describe() is allowed per file (${location.file}).`);
65
+ }
66
+ if (existingFile !== undefined) {
67
+ throw new Error(`${name}("${title}"): duplicate specName "${specName}" in ${location.file} and ${existingFile}. Spec file names must be unique.`);
68
+ }
69
+ specNameFiles.set(specName, location.file);
70
+ internals.describe(type, location, title, pwDetails, () => {
71
+ registerFeatureHubMock(testInstance, options.featureHubResponse);
72
+ if (options.goodmock) {
73
+ registerGoodmockHooks(testInstance, options.goodmock, specName);
74
+ }
75
+ // Workspace settings + additional window properties — merge with parent
76
+ // stack and inject beforeEach. Wraps the callback so nested describes inherit.
77
+ insideRootDescribe = true;
78
+ try {
79
+ withDescribeScope(title, details, () => withDescribeDetails(testInstance, settingsStack, awpStack, details, callback));
80
+ }
81
+ finally {
82
+ // Reset even if the body throws, so the next spec file starts outside again.
83
+ insideRootDescribe = false;
84
+ }
85
+ });
86
+ };
87
+ return describeFunction;
88
+ };
89
+ const makeTest = (name, register) => {
90
+ const testFunction = (...args) => {
91
+ const location = getCallerLocation(testFunction);
92
+ const [title, detailsOrBody, bodyArg] = args;
93
+ // Only the `(title, ...)` forms register a test; the title-less forms
94
+ // (`test.skip()`, `test.skip(condition)`, ...) are used inside tests and describes.
95
+ if (typeof title !== "string") {
96
+ register(location, ...args);
97
+ return;
98
+ }
99
+ if (!insideRootDescribe) {
100
+ throw new Error(`${name}("${title}") must be nested inside a test.describe() block.`);
101
+ }
102
+ const details = typeof detailsOrBody === "function"
103
+ ? undefined
104
+ : detailsOrBody;
105
+ const body = typeof detailsOrBody === "function" ? detailsOrBody : bodyArg;
106
+ assertValidConditions(details?.conditions, `${name}("${title}")`);
107
+ const conditions = combineConditions(testConditions, conditionsStack[conditionsStack.length - 1], details?.conditions);
108
+ assertRequiredConditions(testConditions, conditions, `${name}("${title}")`, location.file);
109
+ if (!shouldRunTest(testConditions, conditions, location.file, [...titleStack, title])) {
110
+ // Conditions not met: register as skipped, whatever the variant (`only`, `fail`, ...).
111
+ internals.modifier("skip", location, title, details ? toPlaywrightDetails(details) : {}, body);
112
+ return;
113
+ }
114
+ if (!details) {
115
+ register(location, title, body);
116
+ return;
117
+ }
118
+ withTestDetails(testInstance, settingsStack, awpStack, location, title, details);
119
+ register(location, title, toPlaywrightDetails(details), body);
120
+ };
121
+ return testFunction;
122
+ };
123
+ return Object.assign(makeTest("test", (location, ...args) => internals.createTest("default", location, ...args)), testInstance, {
124
+ only: makeTest("test.only", (location, ...args) => internals.createTest("only", location, ...args)),
125
+ skip: makeTest("test.skip", (location, ...args) => internals.modifier("skip", location, ...args)),
126
+ fixme: makeTest("test.fixme", (location, ...args) => internals.modifier("fixme", location, ...args)),
127
+ fail: Object.assign(makeTest("test.fail", (location, ...args) => internals.modifier("fail", location, ...args)), testInstance.fail, {
128
+ only: makeTest("test.fail.only", (location, ...args) => internals.createTest("fail.only", location, ...args)),
129
+ }),
130
+ /** Build the overridden `test.describe` (with its variants) for a single `createTest` instance. */
131
+ describe: Object.assign(makeDescribe("default", "test.describe"), testInstance.describe, {
132
+ only: makeDescribe("only", "test.describe.only"),
133
+ fixme: makeDescribe("fixme", "test.describe.fixme"),
134
+ skip: makeDescribe("skip", "test.describe.skip"),
135
+ serial: Object.assign(makeDescribe("serial", "test.describe.serial"), {
136
+ only: makeDescribe("serial.only", "test.describe.serial.only"),
137
+ }),
138
+ parallel: Object.assign(makeDescribe("parallel", "test.describe.parallel"), {
139
+ only: makeDescribe("parallel.only", "test.describe.parallel.only"),
140
+ }),
141
+ }),
142
+ });
143
+ }
@@ -1,7 +1,7 @@
1
1
  import type { Fixtures as PlaywrightFixtures } from "@playwright/test";
2
- import { type BaseTestArgs, type BaseWorkerArgs } from "./playwright-types.js";
2
+ import { type PlaywrightBaseTestArgs, type PlaywrightBaseWorkerArgs } from "./playwright-types.js";
3
3
  /**
4
4
  * @internal
5
5
  */
6
- export type Fixtures<T extends {} = {}, W extends {} = {}> = PlaywrightFixtures<T, W, BaseTestArgs, BaseWorkerArgs>;
6
+ export type Fixtures<T extends {} = {}, W extends {} = {}> = PlaywrightFixtures<T, W, PlaywrightBaseTestArgs, PlaywrightBaseWorkerArgs>;
7
7
  //# sourceMappingURL=fixtures.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/fixtures.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,IAAI,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEvE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,kBAAkB,CAC3E,CAAC,EACD,CAAC,EACD,YAAY,EACZ,cAAc,CACjB,CAAC"}
1
+ {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/fixtures.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,IAAI,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEvE,OAAO,EAAE,KAAK,sBAAsB,EAAE,KAAK,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAEnG;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,kBAAkB,CAC3E,CAAC,EACD,CAAC,EACD,sBAAsB,EACtB,wBAAwB,CAC3B,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { PlaywrightTestInstance } from "./playwright-types.js";
2
+ /**
3
+ * Source location Playwright records for tests, describes and modifiers.
4
+ */
5
+ export interface IPlaywrightLocation {
6
+ file: string;
7
+ line: number;
8
+ column: number;
9
+ }
10
+ export type PlaywrightCreateTestType = "default" | "only" | "fail.only";
11
+ export type PlaywrightModifierType = "skip" | "fixme" | "fail";
12
+ export type PlaywrightDescribeType = "default" | "only" | "skip" | "fixme" | "serial" | "serial.only" | "parallel" | "parallel.only";
13
+ export interface IPlaywrightInternals {
14
+ /** Same as `test(...)`, `test.only(...)`, `test.fail.only(...)`. */
15
+ createTest(type: PlaywrightCreateTestType, location: IPlaywrightLocation, ...args: unknown[]): void;
16
+ /** Same as `test.skip(...)`, `test.fixme(...)`, `test.fail(...)`, in both their title and title-less forms. */
17
+ modifier(type: PlaywrightModifierType, location: IPlaywrightLocation, ...args: unknown[]): void;
18
+ /** Same as `test.describe(...)` and its variants. */
19
+ describe(type: PlaywrightDescribeType, location: IPlaywrightLocation, ...args: unknown[]): void;
20
+ }
21
+ /**
22
+ * Get the private `TestTypeImpl` behind a Playwright `test` function.
23
+ */
24
+ export declare function getPlaywrightInternals(testInstance: PlaywrightTestInstance): IPlaywrightInternals;
25
+ /**
26
+ * Location of whoever called `wrapper` (the spec file, or a consumer helper): the same frame
27
+ * Playwright's public API would have recorded.
28
+ *
29
+ * Frames are cut by function identity (`Error.captureStackTrace` omits `wrapper` and everything above it),
30
+ * not by file path, so this keeps working when consumers bundle this package into their own chunks.
31
+ *
32
+ * Playwright installs source-map-support while loading test files, so the stack already shows
33
+ * the original TypeScript file and line.
34
+ *
35
+ * @param wrapper - our public function the spec called (`test`, `test.describe`, ...); must be on the stack
36
+ */
37
+ export declare function getCallerLocation(wrapper: (...args: never[]) => unknown): IPlaywrightLocation;
38
+ //# sourceMappingURL=playwright-internals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright-internals.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/playwright-internals.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAUpE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,CAAC;AACxE,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAC/D,MAAM,MAAM,sBAAsB,GAC5B,SAAS,GACT,MAAM,GACN,MAAM,GACN,OAAO,GACP,QAAQ,GACR,aAAa,GACb,UAAU,GACV,eAAe,CAAC;AAEtB,MAAM,WAAW,oBAAoB;IACjC,oEAAoE;IACpE,UAAU,CAAC,IAAI,EAAE,wBAAwB,EAAE,QAAQ,EAAE,mBAAmB,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACpG,+GAA+G;IAC/G,QAAQ,CAAC,IAAI,EAAE,sBAAsB,EAAE,QAAQ,EAAE,mBAAmB,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChG,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,sBAAsB,EAAE,QAAQ,EAAE,mBAAmB,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CACnG;AAoBD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,sBAAsB,GAAG,oBAAoB,CAkBjG;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,GAAG,mBAAmB,CAmB7F"}
@@ -0,0 +1,60 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { fileURLToPath } from "url";
3
+ function isTestTypeImpl(value) {
4
+ if (typeof value !== "object" || value === null) {
5
+ return false;
6
+ }
7
+ const impl = value;
8
+ return (typeof impl._createTest === "function" &&
9
+ typeof impl._modifier === "function" &&
10
+ typeof impl._describe === "function");
11
+ }
12
+ /**
13
+ * Get the private `TestTypeImpl` behind a Playwright `test` function.
14
+ */
15
+ export function getPlaywrightInternals(testInstance) {
16
+ // Playwright stores it under a module-private `Symbol("testType")`, so look it up by description.
17
+ const symbol = Object.getOwnPropertySymbols(testInstance).find((s) => s.description === "testType");
18
+ const impl = symbol ? testInstance[symbol] : undefined;
19
+ if (!isTestTypeImpl(impl)) {
20
+ throw new Error("@gooddata/sdk-e2e-utils: Playwright internals (TestTypeImpl._createTest/_modifier/_describe) " +
21
+ "were not found. They changed in this @playwright/test version; update " +
22
+ "src/create-test/playwright/playwright-internals.ts.");
23
+ }
24
+ return {
25
+ createTest: (type, location, ...args) => impl._createTest(type, location, ...args),
26
+ modifier: (type, location, ...args) => impl._modifier(type, location, ...args),
27
+ describe: (type, location, ...args) => impl._describe(type, location, ...args),
28
+ };
29
+ }
30
+ // Matches the location at the end of a stack line: "at fn (/path/file.ts:12:3)", "at /path/file.ts:12:3",
31
+ // "at fn (file:///path/file.ts:12:3)".
32
+ const STACK_FRAME_LOCATION = /\(?((?:file:\/\/)?[^\s()]+):(\d+):(\d+)\)?$/;
33
+ /**
34
+ * Location of whoever called `wrapper` (the spec file, or a consumer helper): the same frame
35
+ * Playwright's public API would have recorded.
36
+ *
37
+ * Frames are cut by function identity (`Error.captureStackTrace` omits `wrapper` and everything above it),
38
+ * not by file path, so this keeps working when consumers bundle this package into their own chunks.
39
+ *
40
+ * Playwright installs source-map-support while loading test files, so the stack already shows
41
+ * the original TypeScript file and line.
42
+ *
43
+ * @param wrapper - our public function the spec called (`test`, `test.describe`, ...); must be on the stack
44
+ */
45
+ export function getCallerLocation(wrapper) {
46
+ const target = {};
47
+ Error.captureStackTrace(target, wrapper);
48
+ for (const stackLine of (target.stack ?? "").split("\n").slice(1)) {
49
+ const match = STACK_FRAME_LOCATION.exec(stackLine.trim());
50
+ if (!match) {
51
+ continue;
52
+ }
53
+ const file = match[1].startsWith("file://") ? fileURLToPath(match[1]) : match[1];
54
+ if (file.startsWith("node:")) {
55
+ continue;
56
+ }
57
+ return { file, line: Number(match[2]), column: Number(match[3]) };
58
+ }
59
+ throw new Error("@gooddata/sdk-e2e-utils: could not determine the caller location from the stack.");
60
+ }
@@ -1,42 +1,34 @@
1
- import type { PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, test } from "@playwright/test";
1
+ import type { PlaywrightTestArgs, PlaywrightTestOptions, TestType as PlaywrightTestType, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestInfo } from "@playwright/test";
2
2
  /**
3
3
  * @internal
4
4
  */
5
- export type BaseTestArgs = PlaywrightTestArgs & PlaywrightTestOptions;
5
+ export type PlaywrightBaseTestArgs = PlaywrightTestArgs & PlaywrightTestOptions;
6
6
  /**
7
7
  * @internal
8
8
  */
9
- export type BaseWorkerArgs = PlaywrightWorkerArgs & PlaywrightWorkerOptions;
9
+ export type PlaywrightBaseWorkerArgs = PlaywrightWorkerArgs & PlaywrightWorkerOptions;
10
10
  /**
11
11
  * @internal
12
12
  */
13
- export type Test = typeof test;
13
+ export type { PlaywrightTestType };
14
14
  /**
15
15
  * @internal
16
16
  */
17
- export type Describe = typeof test.describe;
17
+ export type PlaywrightTestInstance = PlaywrightTestType<PlaywrightTestArgs & PlaywrightTestOptions, PlaywrightWorkerArgs & PlaywrightWorkerOptions>;
18
18
  /**
19
19
  * @internal
20
20
  */
21
- export type DescribeOnly = typeof test.describe.only;
21
+ export type PlaywrightDescribeConfigure = PlaywrightTestInstance["describe"]["configure"];
22
22
  /**
23
23
  * @internal
24
+ * Body of a test: `(fixtures, testInfo) => ...`. Mirrors Playwright's internal `TestBody<TestArgs>`,
25
+ * which `@playwright/test` doesn't export.
24
26
  */
25
- export type DescribeConfigure = typeof test.describe.configure;
27
+ export type PlaywrightTestBody<TestArgs> = (args: TestArgs, testInfo: TestInfo) => Promise<unknown> | unknown;
26
28
  /**
27
29
  * @internal
30
+ * Condition callback of `test.skip(callback, description?)` and friends. Mirrors Playwright's internal
31
+ * `ConditionBody<TestArgs>`, which `@playwright/test` doesn't export.
28
32
  */
29
- export type DescribeFixme = typeof test.describe.fixme;
30
- /**
31
- * @internal
32
- */
33
- export type DescribeSerial = typeof test.describe.serial;
34
- /**
35
- * @internal
36
- */
37
- export type DescribeParallel = typeof test.describe.parallel;
38
- /**
39
- * @internal
40
- */
41
- export type DescribeSkip = typeof test.describe.skip;
33
+ export type PlaywrightConditionBody<TestArgs> = (args: TestArgs) => boolean;
42
34
  //# sourceMappingURL=playwright-types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"playwright-types.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/playwright-types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACR,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,IAAI,EACP,MAAM,kBAAkB,CAAC;AAE1B;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,kBAAkB,GAAG,qBAAqB,CAAC;AAEtE;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,uBAAuB,CAAC;AAE5E;;GAEG;AACH,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC;AAE/B;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,IAAI,CAAC,QAAQ,CAAC;AAE5C;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;AAErD;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;AAE/D;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;AAEvD;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;AAEzD;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;AAE7D;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC"}
1
+ {"version":3,"file":"playwright-types.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/playwright-types.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACR,kBAAkB,EAClB,qBAAqB,EACrB,QAAQ,IAAI,kBAAkB,EAC9B,oBAAoB,EACpB,uBAAuB,EACvB,QAAQ,EACX,MAAM,kBAAkB,CAAC;AAE1B;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,kBAAkB,GAAG,qBAAqB,CAAC;AAEhF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,oBAAoB,GAAG,uBAAuB,CAAC;AAEtF;;GAEG;AACH,YAAY,EAAE,kBAAkB,EAAE,CAAC;AAEnC;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,kBAAkB,CACnD,kBAAkB,GAAG,qBAAqB,EAC1C,oBAAoB,GAAG,uBAAuB,CACjD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC,CAAC;AAE1F;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;AAE9G;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { ITestConditions, TestConditionsValueConstraint } from "./types.js";
2
+ /** Combine a describe's or test's own conditions with the inherited ones (`child` replaces `parent` by default). */
3
+ export declare function combineConditions<TestConditionsValue extends TestConditionsValueConstraint>(testConditions: ITestConditions<TestConditionsValue> | undefined, parent: TestConditionsValue | undefined, child: TestConditionsValue | undefined): TestConditionsValue | undefined;
4
+ /**
5
+ * Throw if `conditions` is set (not `undefined`) but empty or of a type that isn't allowed. Empty values
6
+ * shouldn't be set at all. `0` is a valid number; objects other than plain objects, arrays, `Map` and
7
+ * `Set` (class instances, `Date`, ...) aren't checked for emptiness.
8
+ *
9
+ * @param conditions - the value from `details.conditions`
10
+ * @param where - call description used in the error message, e.g. `test.describe("title")`
11
+ */
12
+ export declare function assertValidConditions(conditions: unknown, where: string): void;
13
+ /**
14
+ * Throw if `testConditions.required` is set and a test ends up without conditions, neither its own
15
+ * nor inherited from an enclosing describe.
16
+ *
17
+ * @param where - call description used in the error message, e.g. `test("title")`
18
+ */
19
+ export declare function assertRequiredConditions<TestConditionsValue extends TestConditionsValueConstraint>(testConditions: ITestConditions<TestConditionsValue> | undefined, conditions: TestConditionsValue | undefined, where: string, file: string): void;
20
+ /**
21
+ * Whether a test runs. Without conditions anywhere in its chain (or without `testConditions`
22
+ * configured), the predicate isn't called and the test runs.
23
+ */
24
+ export declare function shouldRunTest<TestConditionsValue extends TestConditionsValueConstraint>(testConditions: ITestConditions<TestConditionsValue> | undefined, conditions: TestConditionsValue | undefined, file: string, titlePath: string[]): boolean;
25
+ //# sourceMappingURL=test-conditions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-conditions.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/test-conditions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAEjF,oHAAoH;AACpH,wBAAgB,iBAAiB,CAAC,mBAAmB,SAAS,6BAA6B,EACvF,cAAc,EAAE,eAAe,CAAC,mBAAmB,CAAC,GAAG,SAAS,EAChE,MAAM,EAAE,mBAAmB,GAAG,SAAS,EACvC,KAAK,EAAE,mBAAmB,GAAG,SAAS,GACvC,mBAAmB,GAAG,SAAS,CAKjC;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAS9E;AAgCD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,mBAAmB,SAAS,6BAA6B,EAC9F,cAAc,EAAE,eAAe,CAAC,mBAAmB,CAAC,GAAG,SAAS,EAChE,UAAU,EAAE,mBAAmB,GAAG,SAAS,EAC3C,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACb,IAAI,CAON;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,mBAAmB,SAAS,6BAA6B,EACnF,cAAc,EAAE,eAAe,CAAC,mBAAmB,CAAC,GAAG,SAAS,EAChE,UAAU,EAAE,mBAAmB,GAAG,SAAS,EAC3C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EAAE,GACpB,OAAO,CAKT"}
@@ -0,0 +1,76 @@
1
+ // (C) 2026 GoodData Corporation
2
+ /** Combine a describe's or test's own conditions with the inherited ones (`child` replaces `parent` by default). */
3
+ export function combineConditions(testConditions, parent, child) {
4
+ if (child === undefined) {
5
+ return parent;
6
+ }
7
+ return testConditions?.merge ? testConditions.merge(parent, child) : child;
8
+ }
9
+ /**
10
+ * Throw if `conditions` is set (not `undefined`) but empty or of a type that isn't allowed. Empty values
11
+ * shouldn't be set at all. `0` is a valid number; objects other than plain objects, arrays, `Map` and
12
+ * `Set` (class instances, `Date`, ...) aren't checked for emptiness.
13
+ *
14
+ * @param conditions - the value from `details.conditions`
15
+ * @param where - call description used in the error message, e.g. `test.describe("title")`
16
+ */
17
+ export function assertValidConditions(conditions, where) {
18
+ if (conditions === undefined) {
19
+ return;
20
+ }
21
+ const problem = getConditionsProblem(conditions);
22
+ if (problem) {
23
+ throw new Error(`${where}: \`conditions\` ${problem}. Leave \`conditions\` out instead.`);
24
+ }
25
+ }
26
+ function getConditionsProblem(conditions) {
27
+ // `null` and `boolean` are already rejected by the types; this catches values cast past them.
28
+ if (conditions === null) {
29
+ return "is null";
30
+ }
31
+ if (typeof conditions === "boolean") {
32
+ return "is a boolean, which isn't an allowed conditions value";
33
+ }
34
+ if (typeof conditions === "string") {
35
+ return conditions === "" ? "is set but empty (empty string)" : undefined;
36
+ }
37
+ if (typeof conditions === "number") {
38
+ return Number.isNaN(conditions) ? "is set but not a number (NaN)" : undefined;
39
+ }
40
+ if (Array.isArray(conditions)) {
41
+ return conditions.length === 0 ? "is set but empty (empty array)" : undefined;
42
+ }
43
+ if (conditions instanceof Map || conditions instanceof Set) {
44
+ return conditions.size === 0 ? `is set but empty (empty ${conditions.constructor.name})` : undefined;
45
+ }
46
+ if (typeof conditions === "object") {
47
+ const prototype = Object.getPrototypeOf(conditions);
48
+ const isPlainObject = prototype === Object.prototype || prototype === null;
49
+ return isPlainObject && Object.keys(conditions).length === 0
50
+ ? "is set but empty (object without keys)"
51
+ : undefined;
52
+ }
53
+ return undefined;
54
+ }
55
+ /**
56
+ * Throw if `testConditions.required` is set and a test ends up without conditions, neither its own
57
+ * nor inherited from an enclosing describe.
58
+ *
59
+ * @param where - call description used in the error message, e.g. `test("title")`
60
+ */
61
+ export function assertRequiredConditions(testConditions, conditions, where, file) {
62
+ if (testConditions?.required && conditions === undefined) {
63
+ throw new Error(`${where}: \`testConditions.required\` is set, but neither this test nor any enclosing ` +
64
+ `test.describe() sets \`conditions\` (${file}).`);
65
+ }
66
+ }
67
+ /**
68
+ * Whether a test runs. Without conditions anywhere in its chain (or without `testConditions`
69
+ * configured), the predicate isn't called and the test runs.
70
+ */
71
+ export function shouldRunTest(testConditions, conditions, file, titlePath) {
72
+ if (!testConditions || conditions === undefined) {
73
+ return true;
74
+ }
75
+ return testConditions.shouldRun(conditions, { file, titlePath });
76
+ }
@@ -0,0 +1,5 @@
1
+ import type { TestDetails } from "@playwright/test";
2
+ import type { ITestDetails, TestConditionsValueConstraint } from "./types.js";
3
+ /** Strip custom fields before passing details to Playwright. */
4
+ export declare function toPlaywrightDetails(details: ITestDetails<TestConditionsValueConstraint>): TestDetails;
5
+ //# sourceMappingURL=to-playwright-details.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-playwright-details.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/to-playwright-details.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,YAAY,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAE9E,gEAAgE;AAChE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,CAAC,6BAA6B,CAAC,GAAG,WAAW,CAIrG"}
@@ -0,0 +1,7 @@
1
+ // (C) 2026 GoodData Corporation
2
+ /** Strip custom fields before passing details to Playwright. */
3
+ export function toPlaywrightDetails(details) {
4
+ // oxlint-disable-next-line @typescript-eslint/no-unused-vars
5
+ const { workspaceSettings: _ws, additionalWindowProperties: _awp, conditions: _c, ...rest } = details;
6
+ return Object.keys(rest).length > 0 ? rest : {};
7
+ }