@gooddata/sdk-e2e-utils 11.62.0-alpha.2 → 11.62.0-alpha.3

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.
@@ -0,0 +1,8 @@
1
+ import type { IFeatureHubFeature } from "./types.js";
2
+ /**
3
+ * @internal
4
+ * Merge `overrides` into `features`: an override whose `key` already exists in `features` replaces that
5
+ * feature in place, any other override is appended at the end. Neither input is mutated.
6
+ */
7
+ export declare function mergeFeatureHubFeatures(features: IFeatureHubFeature[], overrides: IFeatureHubFeature[]): IFeatureHubFeature[];
8
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../../src/create-test/feature-hub/merge.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,uBAAuB,CACnC,QAAQ,EAAE,kBAAkB,EAAE,EAC9B,SAAS,EAAE,kBAAkB,EAAE,GAChC,kBAAkB,EAAE,CActB"}
@@ -0,0 +1,19 @@
1
+ // (C) 2026 GoodData Corporation
2
+ /**
3
+ * @internal
4
+ * Merge `overrides` into `features`: an override whose `key` already exists in `features` replaces that
5
+ * feature in place, any other override is appended at the end. Neither input is mutated.
6
+ */
7
+ export function mergeFeatureHubFeatures(features, overrides) {
8
+ const merged = [...features];
9
+ for (const override of overrides) {
10
+ const index = merged.findIndex((feature) => feature.key === override.key);
11
+ if (index === -1) {
12
+ merged.push(override);
13
+ }
14
+ else {
15
+ merged[index] = override;
16
+ }
17
+ }
18
+ return merged;
19
+ }
@@ -1,5 +1,13 @@
1
- import type { PlaywrightTestInstance } from "../playwright/playwright-types.js";
2
- import type { IFeatureHubEnvironment } from "./types.js";
1
+ import type { Page } from "@playwright/test";
2
+ import type { IFeatureHubEnvironment, IFeatureHubFeature } from "./types.js";
3
+ /**
4
+ * @internal
5
+ */
6
+ export declare function makeFeatureHubResponse(features: IFeatureHubFeature[]): IFeatureHubEnvironment[];
3
7
  /** FeatureHub mock — each test gets a fresh page, so route dies with it. */
4
- export declare function registerFeatureHubMock(testInstance: PlaywrightTestInstance, featureHubResponse: IFeatureHubEnvironment[]): void;
8
+ export declare function registerFeatureHubMock(page: Page, features: IFeatureHubFeature[]): Promise<void>;
9
+ /** Remove the FeatureHub mock registered by {@link registerFeatureHubMock}. */
10
+ export declare function unregisterFeatureHubMock(page: Page): Promise<void>;
11
+ /** Replace the current FeatureHub mock with one serving `featureHubResponse`. */
12
+ export declare function reregisterFeatureHubMock(page: Page, featureHubResponse: IFeatureHubFeature[]): Promise<void>;
5
13
  //# 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,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"}
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,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAI7E;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,sBAAsB,EAAE,CAO/F;AAED,4EAA4E;AAC5E,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAWtG;AAED,+EAA+E;AAC/E,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAExE;AAED,iFAAiF;AACjF,wBAAsB,wBAAwB,CAC1C,IAAI,EAAE,IAAI,EACV,kBAAkB,EAAE,kBAAkB,EAAE,GACzC,OAAO,CAAC,IAAI,CAAC,CAGf"}
@@ -1,15 +1,34 @@
1
1
  // (C) 2026 GoodData Corporation
2
+ const FEATURE_HUB_ROUTE = "**/features*";
3
+ /**
4
+ * @internal
5
+ */
6
+ export function makeFeatureHubResponse(features) {
7
+ return [
8
+ {
9
+ id: "50e8946c-ef93-4748-bfb1-f0522214cc69",
10
+ features,
11
+ },
12
+ ];
13
+ }
2
14
  /** FeatureHub mock — each test gets a fresh page, so route dies with it. */
3
- export function registerFeatureHubMock(testInstance, featureHubResponse) {
4
- const body = JSON.stringify(featureHubResponse);
5
- testInstance.beforeEach(async ({ page }) => {
6
- await page.route("**/features*", async (route) => {
7
- await route.fulfill({
8
- status: 200,
9
- contentType: "application/json",
10
- headers: { "access-control-allow-origin": "*" },
11
- body,
12
- });
15
+ export async function registerFeatureHubMock(page, features) {
16
+ const body = JSON.stringify(makeFeatureHubResponse(features));
17
+ await page.route(FEATURE_HUB_ROUTE, async (route) => {
18
+ await route.fulfill({
19
+ status: 200,
20
+ contentType: "application/json",
21
+ headers: { "access-control-allow-origin": "*" },
22
+ body,
13
23
  });
14
24
  });
15
25
  }
26
+ /** Remove the FeatureHub mock registered by {@link registerFeatureHubMock}. */
27
+ export async function unregisterFeatureHubMock(page) {
28
+ await page.unroute(FEATURE_HUB_ROUTE);
29
+ }
30
+ /** Replace the current FeatureHub mock with one serving `featureHubResponse`. */
31
+ export async function reregisterFeatureHubMock(page, featureHubResponse) {
32
+ await unregisterFeatureHubMock(page);
33
+ await registerFeatureHubMock(page, featureHubResponse);
34
+ }
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @internal
3
+ */
4
+ export type FeatureHubFeatureValue = boolean | string | number;
1
5
  /**
2
6
  * @internal
3
7
  */
@@ -7,7 +11,7 @@ export interface IFeatureHubFeature {
7
11
  l: boolean;
8
12
  version?: number;
9
13
  type: string;
10
- value?: boolean | string | number;
14
+ value?: FeatureHubFeatureValue;
11
15
  strategies?: unknown[];
12
16
  v?: string;
13
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/create-test/feature-hub/types.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,EAAE,OAAO,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;IACvB,CAAC,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAClC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/create-test/feature-hub/types.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,EAAE,OAAO,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;IACvB,CAAC,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAClC"}
@@ -1 +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"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/factory.ts"],"names":[],"mappings":"AAkBA,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,CAwNrF"}
@@ -15,12 +15,14 @@ import { withDescribeDetails, withTestDetails } from "./with-describe-details.js
15
15
  export function createTest(options) {
16
16
  // Tracks whether we're inside the outermost describe of a spec file during synchronous registration.
17
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.
18
+ // Stacks track workspace settings / additionalWindowProperties / FeatureHub features hierarchy
19
+ // during synchronous describe registration. Each level merges on top of the parent's.
20
20
  // The beforeEach at each level captures the fully-merged snapshot, so the
21
21
  // innermost beforeEach (which runs last) wins.
22
+ // The features stack starts with the defaults, so every level merges on top of them.
22
23
  const settingsStack = [];
23
24
  const awpStack = [];
25
+ const featuresStack = [options.featureHubFeatures];
24
26
  // Combined test conditions and titles of the enclosing describes, during synchronous registration.
25
27
  // Describes only feed these; each test decides for itself whether it runs (a describe whose own
26
28
  // conditions are false may still contain tests whose combined conditions are true).
@@ -53,7 +55,7 @@ export function createTest(options) {
53
55
  assertValidConditions(details.conditions, `${name}("${title}")`);
54
56
  const pwDetails = toPlaywrightDetails(details);
55
57
  if (insideRootDescribe) {
56
- internals.describe(type, location, title, pwDetails, () => withDescribeScope(title, details, () => withDescribeDetails(testInstance, settingsStack, awpStack, details, callback)));
58
+ internals.describe(type, location, title, pwDetails, () => withDescribeScope(title, details, () => withDescribeDetails(testInstance, settingsStack, awpStack, featuresStack, details, callback)));
57
59
  return;
58
60
  }
59
61
  // Outermost describe of the spec file: it also sets up the spec.
@@ -68,7 +70,9 @@ export function createTest(options) {
68
70
  }
69
71
  specNameFiles.set(specName, location.file);
70
72
  internals.describe(type, location, title, pwDetails, () => {
71
- registerFeatureHubMock(testInstance, options.featureHubResponse);
73
+ testInstance.beforeEach(async ({ page }) => {
74
+ await registerFeatureHubMock(page, options.featureHubFeatures);
75
+ });
72
76
  if (options.goodmock) {
73
77
  registerGoodmockHooks(testInstance, options.goodmock, specName);
74
78
  }
@@ -76,7 +80,7 @@ export function createTest(options) {
76
80
  // stack and inject beforeEach. Wraps the callback so nested describes inherit.
77
81
  insideRootDescribe = true;
78
82
  try {
79
- withDescribeScope(title, details, () => withDescribeDetails(testInstance, settingsStack, awpStack, details, callback));
83
+ withDescribeScope(title, details, () => withDescribeDetails(testInstance, settingsStack, awpStack, featuresStack, details, callback));
80
84
  }
81
85
  finally {
82
86
  // Reset even if the body throws, so the next spec file starts outside again.
@@ -115,7 +119,7 @@ export function createTest(options) {
115
119
  register(location, title, body);
116
120
  return;
117
121
  }
118
- withTestDetails(testInstance, settingsStack, awpStack, location, title, details);
122
+ withTestDetails(testInstance, settingsStack, awpStack, featuresStack, location, title, details);
119
123
  register(location, title, toPlaywrightDetails(details), body);
120
124
  };
121
125
  return testFunction;
@@ -0,0 +1,6 @@
1
+ import { type IFeatureHubFeature } from "../feature-hub/types.js";
2
+ import { type FeatureFlagValue, type FeatureFlags } from "./types.js";
3
+ export declare function flagToFeature(key: string, flagValue: FeatureFlagValue): IFeatureHubFeature;
4
+ /** Convert a `featureFlags` record from test details into FeatureHub features. */
5
+ export declare function flagsToFeatures(flags: FeatureFlags): IFeatureHubFeature[];
6
+ //# sourceMappingURL=flag-to-feature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flag-to-feature.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/flag-to-feature.ts"],"names":[],"mappings":"AAIA,OAAO,EAA+B,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE/F,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtE,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,gBAAgB,GAAG,kBAAkB,CAqB1F;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,kBAAkB,EAAE,CAEzE"}
@@ -0,0 +1,27 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { v4 as uuidv4 } from "uuid";
3
+ export function flagToFeature(key, flagValue) {
4
+ const flagValueType = typeof flagValue;
5
+ let valueType;
6
+ let value;
7
+ if (flagValueType === "object") {
8
+ value = JSON.stringify(flagValue);
9
+ valueType = "JSON";
10
+ }
11
+ else {
12
+ value = flagValue;
13
+ valueType = flagValueType.toUpperCase();
14
+ }
15
+ return {
16
+ id: uuidv4(),
17
+ key,
18
+ l: false,
19
+ version: 1,
20
+ type: valueType,
21
+ value,
22
+ };
23
+ }
24
+ /** Convert a `featureFlags` record from test details into FeatureHub features. */
25
+ export function flagsToFeatures(flags) {
26
+ return Object.entries(flags).map(([key, value]) => flagToFeature(key, value));
27
+ }
@@ -1 +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"}
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,CAUrG"}
@@ -2,6 +2,6 @@
2
2
  /** Strip custom fields before passing details to Playwright. */
3
3
  export function toPlaywrightDetails(details) {
4
4
  // oxlint-disable-next-line @typescript-eslint/no-unused-vars
5
- const { workspaceSettings: _ws, additionalWindowProperties: _awp, conditions: _c, ...rest } = details;
5
+ const { workspaceSettings: _ws, additionalWindowProperties: _awp, featureFlags: _ff, conditions: _c, ...rest } = details;
6
6
  return Object.keys(rest).length > 0 ? rest : {};
7
7
  }
@@ -1,5 +1,5 @@
1
1
  import type { Fixtures, TestDetails } from "@playwright/test";
2
- import type { IFeatureHubEnvironment } from "../feature-hub/types.js";
2
+ import type { FeatureHubFeatureValue, IFeatureHubFeature } from "../feature-hub/types.js";
3
3
  import type { IGoodmockOptions } from "../goodmock/types.js";
4
4
  import type { PlaywrightConditionBody, PlaywrightDescribeConfigure, PlaywrightTestBody, PlaywrightTestType } from "./playwright-types.js";
5
5
  /**
@@ -79,7 +79,7 @@ export interface ITestConditions<TestConditionsValue extends TestConditionsValue
79
79
  * @internal
80
80
  */
81
81
  export interface ICustomCreateTestOptions<TestConditionsValue extends TestConditionsValueConstraint = never> {
82
- featureHubResponse: IFeatureHubEnvironment[];
82
+ featureHubFeatures: IFeatureHubFeature[];
83
83
  goodmock?: IGoodmockOptions;
84
84
  testConditions?: ITestConditions<TestConditionsValue>;
85
85
  }
@@ -100,19 +100,30 @@ export type WorkspaceSettings = Record<string, unknown>;
100
100
  * @internal
101
101
  */
102
102
  export type WindowProperties = Record<string, unknown>;
103
+ /**
104
+ * @internal
105
+ */
106
+ export type FeatureFlagValue = FeatureHubFeatureValue | object;
107
+ /**
108
+ * @internal
109
+ */
110
+ export type FeatureFlags = Record<string, FeatureHubFeatureValue>;
103
111
  /**
104
112
  * @internal
105
113
  */
106
114
  export interface ITestDetails<TestConditionsValue extends TestConditionsValueConstraint = never> extends TestDetails {
107
115
  workspaceSettings?: WorkspaceSettings;
108
116
  additionalWindowProperties?: WindowProperties;
117
+ /** Feature flags for this describe/test, merged on top of the enclosing describes' (own keys win). */
118
+ featureFlags?: FeatureFlags;
109
119
  /** See {@link ITestConditions}. */
110
120
  conditions?: TestConditionsValue;
111
121
  }
112
122
  /**
113
123
  * @internal
114
124
  * Call signatures that register a test: `test`, `test.only`, `test.fail.only`.
115
- * Same as Playwright's, except `details` also accepts `workspaceSettings` / `additionalWindowProperties` / `conditions`.
125
+ * Same as Playwright's, except `details` also accepts `workspaceSettings` / `additionalWindowProperties` /
126
+ * `featureFlags` / `conditions`.
116
127
  */
117
128
  export interface ITestFunction<TestConditionsValue extends TestConditionsValueConstraint, TestArgs extends {}, WorkerArgs extends {}> {
118
129
  (title: string, body: PlaywrightTestBody<TestArgs & WorkerArgs>): void;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/types.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,13 @@
1
+ import type { IFeatureHubFeature } from "../feature-hub/types.js";
1
2
  import type { IPlaywrightLocation } from "./playwright-internals.js";
2
3
  import type { PlaywrightTestInstance } from "./playwright-types.js";
3
4
  import type { Callback, ITestDetails, TestConditionsValueConstraint, WindowProperties, WorkspaceSettings } from "./types.js";
4
- /** Push merged settings/awp onto stacks, register a single beforeEach, call fn, pop. */
5
- export declare function withDescribeDetails(testInstance: PlaywrightTestInstance, settingsStack: WorkspaceSettings[], awpStack: WindowProperties[], details: ITestDetails<TestConditionsValueConstraint> | undefined, fn: Callback): void;
5
+ /** Push merged settings/awp/features onto stacks, register the beforeEach hooks, call fn, pop. */
6
+ export declare function withDescribeDetails(testInstance: PlaywrightTestInstance, settingsStack: WorkspaceSettings[], awpStack: WindowProperties[], featuresStack: IFeatureHubFeature[][], details: ITestDetails<TestConditionsValueConstraint> | undefined, fn: Callback): void;
6
7
  /**
7
- * Register a single test's own settings/awp, merged on top of the enclosing describes'.
8
+ * Register a single test's own settings/awp/features, merged on top of the enclosing describes'.
8
9
  * The beforeEach is registered next to the test, after the enclosing describes' injection hooks,
9
10
  * so it runs last and its values win.
10
11
  */
11
- export declare function withTestDetails(testInstance: PlaywrightTestInstance, settingsStack: WorkspaceSettings[], awpStack: WindowProperties[], location: IPlaywrightLocation, title: string, details: ITestDetails<TestConditionsValueConstraint> | undefined): void;
12
+ export declare function withTestDetails(testInstance: PlaywrightTestInstance, settingsStack: WorkspaceSettings[], awpStack: WindowProperties[], featuresStack: IFeatureHubFeature[][], location: IPlaywrightLocation, title: string, details: ITestDetails<TestConditionsValueConstraint> | undefined): void;
12
13
  //# sourceMappingURL=with-describe-details.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"with-describe-details.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/with-describe-details.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,KAAK,EACR,QAAQ,EACR,YAAY,EACZ,6BAA6B,EAC7B,gBAAgB,EAChB,iBAAiB,EACpB,MAAM,YAAY,CAAC;AAMpB,wFAAwF;AACxF,wBAAgB,mBAAmB,CAC/B,YAAY,EAAE,sBAAsB,EACpC,aAAa,EAAE,iBAAiB,EAAE,EAClC,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,OAAO,EAAE,YAAY,CAAC,6BAA6B,CAAC,GAAG,SAAS,EAChE,EAAE,EAAE,QAAQ,QAsBf;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC3B,YAAY,EAAE,sBAAsB,EACpC,aAAa,EAAE,iBAAiB,EAAE,EAClC,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,YAAY,CAAC,6BAA6B,CAAC,GAAG,SAAS,GACjE,IAAI,CAsBN"}
1
+ {"version":3,"file":"with-describe-details.d.ts","sourceRoot":"","sources":["../../../src/create-test/playwright/with-describe-details.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAGlE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,KAAK,EACR,QAAQ,EACR,YAAY,EACZ,6BAA6B,EAC7B,gBAAgB,EAChB,iBAAiB,EACpB,MAAM,YAAY,CAAC;AAUpB,kGAAkG;AAClG,wBAAgB,mBAAmB,CAC/B,YAAY,EAAE,sBAAsB,EACpC,aAAa,EAAE,iBAAiB,EAAE,EAClC,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,aAAa,EAAE,kBAAkB,EAAE,EAAE,EACrC,OAAO,EAAE,YAAY,CAAC,6BAA6B,CAAC,GAAG,SAAS,EAChE,EAAE,EAAE,QAAQ,QAiCf;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC3B,YAAY,EAAE,sBAAsB,EACpC,aAAa,EAAE,iBAAiB,EAAE,EAClC,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,aAAa,EAAE,kBAAkB,EAAE,EAAE,EACrC,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,YAAY,CAAC,6BAA6B,CAAC,GAAG,SAAS,GACjE,IAAI,CA+BN"}
@@ -1,27 +1,46 @@
1
1
  // (C) 2026 GoodData Corporation
2
+ import { mergeFeatureHubFeatures } from "../feature-hub/merge.js";
3
+ import { reregisterFeatureHubMock } from "../feature-hub/mock.js";
4
+ import { flagsToFeatures } from "./flag-to-feature.js";
2
5
  function peekOrEmpty(stack) {
3
6
  return stack.length > 0 ? stack[stack.length - 1] : {};
4
7
  }
5
- /** Push merged settings/awp onto stacks, register a single beforeEach, call fn, pop. */
6
- export function withDescribeDetails(testInstance, settingsStack, awpStack, details, fn) {
8
+ function peekFeatures(stack) {
9
+ return stack.length > 0 ? stack[stack.length - 1] : [];
10
+ }
11
+ /** Push merged settings/awp/features onto stacks, register the beforeEach hooks, call fn, pop. */
12
+ export function withDescribeDetails(testInstance, settingsStack, awpStack, featuresStack, details, fn) {
7
13
  const ws = details?.workspaceSettings;
8
14
  const awp = details?.additionalWindowProperties;
9
- if (!ws && !awp) {
15
+ const ff = details?.featureFlags;
16
+ if (!ws && !awp && !ff) {
10
17
  fn();
11
18
  return;
12
19
  }
13
20
  const mergedSettings = ws ? { ...peekOrEmpty(settingsStack), ...ws } : peekOrEmpty(settingsStack);
14
21
  const mergedAwp = awp ? { ...peekOrEmpty(awpStack), ...awp } : peekOrEmpty(awpStack);
22
+ const mergedFeatures = ff
23
+ ? mergeFeatureHubFeatures(peekFeatures(featuresStack), flagsToFeatures(ff))
24
+ : peekFeatures(featuresStack);
15
25
  if (ws)
16
26
  settingsStack.push(mergedSettings);
17
27
  if (awp)
18
28
  awpStack.push(mergedAwp);
19
- injectWindowProperties(testInstance, mergedSettings, mergedAwp);
29
+ if (ff)
30
+ featuresStack.push(mergedFeatures);
31
+ if (ws || awp) {
32
+ injectWindowProperties(testInstance, mergedSettings, mergedAwp);
33
+ }
34
+ if (ff) {
35
+ injectFeatureHubMock(testInstance, mergedFeatures);
36
+ }
20
37
  try {
21
38
  fn();
22
39
  }
23
40
  finally {
24
41
  // Pop even if the body throws, so later describes don't inherit this level's entries.
42
+ if (ff)
43
+ featuresStack.pop();
25
44
  if (awp)
26
45
  awpStack.pop();
27
46
  if (ws)
@@ -29,18 +48,22 @@ export function withDescribeDetails(testInstance, settingsStack, awpStack, detai
29
48
  }
30
49
  }
31
50
  /**
32
- * Register a single test's own settings/awp, merged on top of the enclosing describes'.
51
+ * Register a single test's own settings/awp/features, merged on top of the enclosing describes'.
33
52
  * The beforeEach is registered next to the test, after the enclosing describes' injection hooks,
34
53
  * so it runs last and its values win.
35
54
  */
36
- export function withTestDetails(testInstance, settingsStack, awpStack, location, title, details) {
55
+ export function withTestDetails(testInstance, settingsStack, awpStack, featuresStack, location, title, details) {
37
56
  const ws = details?.workspaceSettings;
38
57
  const awp = details?.additionalWindowProperties;
39
- if (!ws && !awp) {
58
+ const ff = details?.featureFlags;
59
+ if (!ws && !awp && !ff) {
40
60
  return;
41
61
  }
42
62
  const mergedSettings = ws ? { ...peekOrEmpty(settingsStack), ...ws } : peekOrEmpty(settingsStack);
43
63
  const mergedAwp = awp ? { ...peekOrEmpty(awpStack), ...awp } : peekOrEmpty(awpStack);
64
+ const mergedFeatures = ff
65
+ ? mergeFeatureHubFeatures(peekFeatures(featuresStack), flagsToFeatures(ff))
66
+ : peekFeatures(featuresStack);
44
67
  testInstance.beforeEach(async ({ page }, testInfo) => {
45
68
  // A beforeEach applies to every test in its describe; only act for this test. Title is part
46
69
  // of the match because tests generated in a loop share one location.
@@ -50,7 +73,12 @@ export function withTestDetails(testInstance, settingsStack, awpStack, location,
50
73
  testInfo.column !== location.column) {
51
74
  return;
52
75
  }
53
- await addWindowPropertiesInitScript(page, mergedSettings, mergedAwp);
76
+ if (ws || awp) {
77
+ await addWindowPropertiesInitScript(page, mergedSettings, mergedAwp);
78
+ }
79
+ if (ff) {
80
+ await reregisterFeatureHubMock(page, mergedFeatures);
81
+ }
54
82
  });
55
83
  }
56
84
  /** Inject workspace settings and additional window properties via a single addInitScript. */
@@ -59,6 +87,12 @@ function injectWindowProperties(testInst, settings, awp) {
59
87
  await addWindowPropertiesInitScript(page, settings, awp);
60
88
  });
61
89
  }
90
+ /** Replace the FeatureHub mock registered by the root describe with one serving `features`. */
91
+ function injectFeatureHubMock(testInst, features) {
92
+ testInst.beforeEach(async ({ page }) => {
93
+ await reregisterFeatureHubMock(page, features);
94
+ });
95
+ }
62
96
  async function addWindowPropertiesInitScript(page, settings, awp) {
63
97
  await page.addInitScript((args) => {
64
98
  const w = window;
package/esm/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { authHeader, authedRouteFetch, injectAuthHeader } from "./auth.js";
2
2
  export { GOODMOCK_HOST, API_TOKEN, BACKEND_HOST, getBaseUrl, getEnvWithFallback, getWorkspaceId, getDangerWorkspaceId, } from "./constants.js";
3
- export type { IFeatureHubEnvironment, IFeatureHubFeature } from "./create-test/feature-hub/types.js";
3
+ export type { IFeatureHubEnvironment, IFeatureHubFeature, FeatureHubFeatureValue, } from "./create-test/feature-hub/types.js";
4
+ export { makeFeatureHubResponse } from "./create-test/feature-hub/mock.js";
4
5
  export type { IGoodmockMapping, ILeakPattern, ISecretMapping, IWorkspaceIdMapping, IGoodmockOptions, } from "./create-test/goodmock/types.js";
5
6
  export { loadMappings, resetMappings, resetScenarios, mockLogRequests, startRecording, } from "./create-test/goodmock/admin.js";
6
7
  export { goodmockMode, GoodmockMode } from "./create-test/goodmock/mode.js";
@@ -8,6 +9,6 @@ export { snapshotAndSaveRecording, type ISnapshotAndSaveRecordingOptions, } from
8
9
  export { createTest } from "./create-test/playwright/factory.js";
9
10
  export type { Fixtures } from "./create-test/playwright/fixtures.js";
10
11
  export type { PlaywrightBaseTestArgs, PlaywrightBaseWorkerArgs, PlaywrightDescribeConfigure, PlaywrightConditionBody, PlaywrightTestBody, PlaywrightTestType, PlaywrightTestInstance, } from "./create-test/playwright/playwright-types.js";
11
- export type { Callback, ICreateTestOptions, ICustomCreateTestOptions, IDescribe, IDescribeFunction, IDescribeModeFunction, ITestConditions, ITestConditionsContext, ITestDetails, ITestFailFunction, ITestFunction, ITestModifierFunction, Test, TestConditionsValueConstraint, WindowProperties, WorkspaceSettings, } from "./create-test/playwright/types.js";
12
+ export type { Callback, FeatureFlags, ICreateTestOptions, ICustomCreateTestOptions, IDescribe, IDescribeFunction, IDescribeModeFunction, ITestConditions, ITestConditionsContext, ITestDetails, ITestFailFunction, ITestFunction, ITestModifierFunction, Test, TestConditionsValueConstraint, WindowProperties, WorkspaceSettings, } from "./create-test/playwright/types.js";
12
13
  export { clickByBoundingBox, hoverByBoundingBox } from "./helpers/mouse-actions.js";
13
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAG3E,OAAO,EACH,aAAa,EACb,SAAS,EACT,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,oBAAoB,GACvB,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAGrG,YAAY,EACR,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,gBAAgB,GACnB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACH,YAAY,EACZ,aAAa,EACb,cAAc,EACd,eAAe,EACf,cAAc,GACjB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC5E,OAAO,EACH,wBAAwB,EACxB,KAAK,gCAAgC,GACxC,MAAM,gEAAgE,CAAC;AAGxE,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACjE,YAAY,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AACrE,YAAY,EACR,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,EAC3B,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,GACzB,MAAM,8CAA8C,CAAC;AACtD,YAAY,EACR,QAAQ,EACR,kBAAkB,EAClB,wBAAwB,EACxB,SAAS,EACT,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,sBAAsB,EACtB,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,IAAI,EACJ,6BAA6B,EAC7B,gBAAgB,EAChB,iBAAiB,GACpB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAG3E,OAAO,EACH,aAAa,EACb,SAAS,EACT,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,oBAAoB,GACvB,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EACR,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,GACzB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAG3E,YAAY,EACR,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,gBAAgB,GACnB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACH,YAAY,EACZ,aAAa,EACb,cAAc,EACd,eAAe,EACf,cAAc,GACjB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC5E,OAAO,EACH,wBAAwB,EACxB,KAAK,gCAAgC,GACxC,MAAM,gEAAgE,CAAC;AAGxE,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACjE,YAAY,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AACrE,YAAY,EACR,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,EAC3B,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,GACzB,MAAM,8CAA8C,CAAC;AACtD,YAAY,EACR,QAAQ,EACR,YAAY,EACZ,kBAAkB,EAClB,wBAAwB,EACxB,SAAS,EACT,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,sBAAsB,EACtB,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,IAAI,EACJ,6BAA6B,EAC7B,gBAAgB,EAChB,iBAAiB,GACpB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC"}
package/esm/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
  export { authHeader, authedRouteFetch, injectAuthHeader } from "./auth.js";
5
5
  // constants
6
6
  export { GOODMOCK_HOST, API_TOKEN, BACKEND_HOST, getBaseUrl, getEnvWithFallback, getWorkspaceId, getDangerWorkspaceId, } from "./constants.js";
7
+ export { makeFeatureHubResponse } from "./create-test/feature-hub/mock.js";
7
8
  export { loadMappings, resetMappings, resetScenarios, mockLogRequests, startRecording, } from "./create-test/goodmock/admin.js";
8
9
  export { goodmockMode, GoodmockMode } from "./create-test/goodmock/mode.js";
9
10
  export { snapshotAndSaveRecording, } from "./create-test/goodmock/snapshot/snapshot-and-save-recording.js";
@@ -59,6 +59,16 @@ export declare function clickByBoundingBox(page: Page, locator: Locator, options
59
59
  */
60
60
  export declare function createTest<TestConditionsValue extends TestConditionsValueConstraint = never, T extends {} = {}, W extends {} = {}>(options: ICreateTestOptions<TestConditionsValue, T, W>): Test<TestConditionsValue, PlaywrightBaseTestArgs & T, PlaywrightBaseWorkerArgs & W>;
61
61
 
62
+ /**
63
+ * @internal
64
+ */
65
+ export declare type FeatureFlags = Record<string, FeatureHubFeatureValue>;
66
+
67
+ /**
68
+ * @internal
69
+ */
70
+ export declare type FeatureHubFeatureValue = boolean | string | number;
71
+
62
72
  /**
63
73
  * @internal
64
74
  */
@@ -127,7 +137,7 @@ export declare type ICreateTestOptions<TestConditionsValue extends TestCondition
127
137
  * @internal
128
138
  */
129
139
  export declare interface ICustomCreateTestOptions<TestConditionsValue extends TestConditionsValueConstraint = never> {
130
- featureHubResponse: IFeatureHubEnvironment[];
140
+ featureHubFeatures: IFeatureHubFeature[];
131
141
  goodmock?: IGoodmockOptions;
132
142
  testConditions?: ITestConditions<TestConditionsValue>;
133
143
  }
@@ -178,7 +188,7 @@ export declare interface IFeatureHubFeature {
178
188
  l: boolean;
179
189
  version?: number;
180
190
  type: string;
181
- value?: boolean | string | number;
191
+ value?: FeatureHubFeatureValue;
182
192
  strategies?: unknown[];
183
193
  v?: string;
184
194
  }
@@ -330,6 +340,8 @@ export declare interface ITestConditionsContext {
330
340
  export declare interface ITestDetails<TestConditionsValue extends TestConditionsValueConstraint = never> extends TestDetails {
331
341
  workspaceSettings?: WorkspaceSettings;
332
342
  additionalWindowProperties?: WindowProperties;
343
+ /** Feature flags for this describe/test, merged on top of the enclosing describes' (own keys win). */
344
+ featureFlags?: FeatureFlags;
333
345
  /** See {@link ITestConditions}. */
334
346
  conditions?: TestConditionsValue;
335
347
  }
@@ -345,7 +357,8 @@ export declare interface ITestFailFunction<TestConditionsValue extends TestCondi
345
357
  /**
346
358
  * @internal
347
359
  * Call signatures that register a test: `test`, `test.only`, `test.fail.only`.
348
- * Same as Playwright's, except `details` also accepts `workspaceSettings` / `additionalWindowProperties` / `conditions`.
360
+ * Same as Playwright's, except `details` also accepts `workspaceSettings` / `additionalWindowProperties` /
361
+ * `featureFlags` / `conditions`.
349
362
  */
350
363
  export declare interface ITestFunction<TestConditionsValue extends TestConditionsValueConstraint, TestArgs extends {}, WorkerArgs extends {}> {
351
364
  (title: string, body: PlaywrightTestBody<TestArgs & WorkerArgs>): void;
@@ -379,6 +392,11 @@ export declare interface IWorkspaceIdMapping {
379
392
  */
380
393
  export declare function loadMappings(host: string, mappingFilePath: string): Promise<void>;
381
394
 
395
+ /**
396
+ * @internal
397
+ */
398
+ export declare function makeFeatureHubResponse(features: IFeatureHubFeature[]): IFeatureHubEnvironment[];
399
+
382
400
  /**
383
401
  * @internal
384
402
  * Add a goodmock stub that mocks log requests (POST /gdc/app/projects/.../log).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddata/sdk-e2e-utils",
3
- "version": "11.62.0-alpha.2",
3
+ "version": "11.62.0-alpha.3",
4
4
  "description": "GoodData utility functions for Playwright E2E tests",
5
5
  "license": "MIT",
6
6
  "author": "GoodData",
@@ -23,7 +23,8 @@
23
23
  }
24
24
  },
25
25
  "dependencies": {
26
- "@playwright/test": "1.62.1"
26
+ "@playwright/test": "1.62.1",
27
+ "uuid": "11.1.1"
27
28
  },
28
29
  "devDependencies": {
29
30
  "@microsoft/api-documenter": "7.30.16",
@@ -49,8 +50,8 @@
49
50
  "oxlint-plugin-eslint": "1.82.0",
50
51
  "oxlint-tsgolint": "7.0.2001",
51
52
  "typescript": "7.0.2",
52
- "@gooddata/eslint-config": "11.62.0-alpha.2",
53
- "@gooddata/oxlint-config": "11.62.0-alpha.2"
53
+ "@gooddata/eslint-config": "11.62.0-alpha.3",
54
+ "@gooddata/oxlint-config": "11.62.0-alpha.3"
54
55
  },
55
56
  "scripts": {
56
57
  "_phase:build": "npm run build",