@gooddata/sdk-e2e-utils 11.31.0-alpha.6

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/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2020-2022 GoodData Corporation
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/esm/auth.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { type Page } from "@playwright/test";
2
+ /**
3
+ * @internal
4
+ * Injects Authorization bearer token into all /api/ requests via Playwright route interception.
5
+ */
6
+ export declare function injectAuthHeader(page: Page, token: string): Promise<void>;
7
+ /**
8
+ * @internal
9
+ */
10
+ export declare const authHeader: (token: string) => {
11
+ authorization: string;
12
+ };
13
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS/E;AAED;;GAEG;AACH,eAAO,MAAM,UAAU;;CAA4D,CAAC"}
package/esm/auth.js ADDED
@@ -0,0 +1,20 @@
1
+ // (C) 2026 GoodData Corporation
2
+ /**
3
+ * @internal
4
+ * Injects Authorization bearer token into all /api/ requests via Playwright route interception.
5
+ */
6
+ export async function injectAuthHeader(page, token) {
7
+ await page.unroute("**/api/**");
8
+ await page.route("**/api/**", async (route) => {
9
+ const headers = {
10
+ ...route.request().headers(),
11
+ authorization: `Bearer ${token}`,
12
+ };
13
+ await route.fallback({ headers });
14
+ });
15
+ }
16
+ /**
17
+ * @internal
18
+ */
19
+ export const authHeader = (token) => ({ authorization: `Bearer ${token}` });
20
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAIhC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAU,EAAE,KAAa,EAAiB;IAC7E,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAChC,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG;YACZ,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE;YAC5B,aAAa,EAAE,UAAU,KAAK,EAAE;SACnC,CAAC;QACF,MAAM,KAAK,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAAA,CACrC,CAAC,CAAC;AAAA,CACN;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,CAAC,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @internal
3
+ */
4
+ export declare const GOODMOCK_HOST: string;
5
+ /**
6
+ * @internal
7
+ */
8
+ export declare const API_TOKEN: string;
9
+ /**
10
+ * @internal
11
+ */
12
+ export declare const BACKEND_HOST: string;
13
+ /**
14
+ * @internal
15
+ */
16
+ export declare const getEnvWithFallback: (env: string, fallback?: string) => string;
17
+ /**
18
+ * @internal
19
+ */
20
+ export declare const getBaseUrl: (defaultValue?: string) => string;
21
+ /**
22
+ * @internal
23
+ */
24
+ export declare const getWorkspaceId: (defaultValue?: string) => string;
25
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,eAAO,MAAM,aAAa,QAAqC,CAAC;AAChE;;GAEG;AACH,eAAO,MAAM,SAAS,QAAuC,CAAC;AAC9D;;GAEG;AACH,eAAO,MAAM,YAAY,QAA4B,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,kBAAkB,4CAA+D,CAAC;AAE/F;;GAEG;AACH,eAAO,MAAM,UAAU,mCAAsE,CAAC;AAC9F;;GAEG;AACH,eAAO,MAAM,cAAc,mCAA+E,CAAC"}
@@ -0,0 +1,26 @@
1
+ // (C) 2026 GoodData Corporation
2
+ /**
3
+ * @internal
4
+ */
5
+ export const GOODMOCK_HOST = process.env["GOODMOCK_HOST"] || "";
6
+ /**
7
+ * @internal
8
+ */
9
+ export const API_TOKEN = process.env["TIGER_API_TOKEN"] ?? "";
10
+ /**
11
+ * @internal
12
+ */
13
+ export const BACKEND_HOST = process.env["HOST"] || "";
14
+ /**
15
+ * @internal
16
+ */
17
+ export const getEnvWithFallback = (env, fallback = "") => process.env[env] || fallback;
18
+ /**
19
+ * @internal
20
+ */
21
+ export const getBaseUrl = (defaultValue = "") => getEnvWithFallback("BASE_URL", defaultValue);
22
+ /**
23
+ * @internal
24
+ */
25
+ export const getWorkspaceId = (defaultValue = "") => getEnvWithFallback("TEST_WORKSPACE_ID", defaultValue);
26
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAEhC;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;AAChE;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC;AAC9D;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;AAEtD;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAAE,QAAQ,GAAG,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC;AAE/F;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,YAAY,GAAG,EAAE,EAAE,EAAE,CAAC,kBAAkB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AAC9F;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,YAAY,GAAG,EAAE,EAAE,EAAE,CAAC,kBAAkB,CAAC,mBAAmB,EAAE,YAAY,CAAC,CAAC"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @internal
3
+ * Load goodmock stub mappings from a JSON file on disk.
4
+ *
5
+ * @param host - Goodmock host:port (e.g. "backend-mock:8080")
6
+ * @param mappingFilePath - Absolute path to the mapping JSON file
7
+ */
8
+ export declare function loadMappings(host: string, mappingFilePath: string): Promise<void>;
9
+ /**
10
+ * @internal
11
+ * Reset goodmock scenario state (sequence counters) without clearing mappings.
12
+ * Must be called between tests so scenario-driven stubs start from the
13
+ * beginning for every test — mirrors the Cypress `resetRecordingsScenarios` task.
14
+ */
15
+ export declare function resetScenarios(host: string): Promise<void>;
16
+ /**
17
+ * @internal
18
+ * Clear all goodmock stub mappings.
19
+ */
20
+ export declare function resetMappings(host: string): Promise<void>;
21
+ /**
22
+ * @internal
23
+ * Add a goodmock stub that mocks log requests (POST /gdc/app/projects/.../log).
24
+ * This prevents the app from failing when it tries to send logs.
25
+ */
26
+ export declare function mockLogRequests(host: string): Promise<void>;
27
+ /**
28
+ * @internal
29
+ * Which mode goodmock is running in
30
+ */
31
+ export declare enum GoodmockMode {
32
+ Replay = "replay",
33
+ Record = "record",
34
+ Proxy = "proxy"
35
+ }
36
+ /**
37
+ * @internal
38
+ */
39
+ export declare function goodmockMode(): GoodmockMode;
40
+ /**
41
+ * @internal
42
+ * Add a catch-all proxy mapping so goodmock forwards every request to the
43
+ * real backend and records the interactions. Must be called after
44
+ * {@link resetMappings} in recording mode.
45
+ */
46
+ export declare function startRecording(host: string, backendHost: string): Promise<void>;
47
+ export interface IGoodmockMapping {
48
+ request: {
49
+ url?: string;
50
+ bodyPatterns?: unknown;
51
+ };
52
+ response: {
53
+ headers?: Record<string, string>;
54
+ jsonBody?: Map<string, any>;
55
+ };
56
+ }
57
+ /**
58
+ * @internal
59
+ */
60
+ export interface IWorkspaceIdMapping {
61
+ sourceWorkspaceId: string;
62
+ targetWorkspaceId: string;
63
+ }
64
+ /**
65
+ * @internal
66
+ * Snapshot goodmock recordings, save the combined mappings to a JSON file
67
+ * on disk, and sanitize any credentials from the output.
68
+ *
69
+ * This mirrors the Cypress recording flow: two snapshot calls are made —
70
+ * one for executionResults URLs (with repeatsAsScenarios) and one for
71
+ * everything else — then the results are merged and written out.
72
+ *
73
+ * @param host -
74
+ * @param mappingFilePath -
75
+ * @param workspaceIdMappings - Optional source/target workspace ID rewrite(s)
76
+ * applied before mappings are saved. Accepts a single mapping or an array.
77
+ * @param backendHost - Optional real backend URL (e.g. "https://example.gooddata.com").
78
+ * When provided together with baseUrl, all occurrences are replaced in the
79
+ * saved mappings so that recorded responses (e.g. geo tile URLs) resolve
80
+ * correctly during replay.
81
+ * @param baseUrl - Optional app base URL (e.g. "http://kpi-dashboards-ui:9500")
82
+ * that replaces backendHost references in the saved mappings.
83
+ */
84
+ export declare function snapshotAndSaveRecording(host: string, mappingFilePath: string, workspaceIdMappings?: IWorkspaceIdMapping | IWorkspaceIdMapping[], backendHost?: string, baseUrl?: string): Promise<void>;
85
+ //# sourceMappingURL=goodmock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goodmock.d.ts","sourceRoot":"","sources":["../src/goodmock.ts"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBvF;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhE;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM/D;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBjE;AAED;;;GAGG;AACH,oBAAY,YAAY;IACpB,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,KAAK,UAAU;CAClB;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,YAAY,CAE3C;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWrF;AAWD,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAClD,QAAQ,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,CAAC;CAC/E;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;CAC7B;AAqBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,wBAAwB,CAC1C,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,MAAM,EACvB,mBAAmB,CAAC,EAAE,mBAAmB,GAAG,mBAAmB,EAAE,EACjE,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAqFf"}
@@ -0,0 +1,227 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { mkdirSync, readFileSync, writeFileSync } from "fs";
3
+ import { dirname } from "path";
4
+ /**
5
+ * @internal
6
+ * Load goodmock stub mappings from a JSON file on disk.
7
+ *
8
+ * @param host - Goodmock host:port (e.g. "backend-mock:8080")
9
+ * @param mappingFilePath - Absolute path to the mapping JSON file
10
+ */
11
+ export async function loadMappings(host, mappingFilePath) {
12
+ let mappings;
13
+ try {
14
+ mappings = readFileSync(mappingFilePath, "utf-8");
15
+ }
16
+ catch {
17
+ console.warn(`No mapping file found: ${mappingFilePath}, skipping`);
18
+ return;
19
+ }
20
+ let json;
21
+ try {
22
+ json = JSON.parse(mappings);
23
+ }
24
+ catch {
25
+ console.error(`Failed to parse mapping file: ${mappingFilePath}`);
26
+ return;
27
+ }
28
+ const response = await fetch(`http://${host}/__admin/mappings/import`, {
29
+ method: "POST",
30
+ headers: { "Content-Type": "application/json" },
31
+ body: JSON.stringify(json),
32
+ });
33
+ // eslint-disable-next-line no-console
34
+ console.log(`Goodmock mappings loaded from ${mappingFilePath} (status: ${response.status})`);
35
+ }
36
+ /**
37
+ * @internal
38
+ * Reset goodmock scenario state (sequence counters) without clearing mappings.
39
+ * Must be called between tests so scenario-driven stubs start from the
40
+ * beginning for every test — mirrors the Cypress `resetRecordingsScenarios` task.
41
+ */
42
+ export async function resetScenarios(host) {
43
+ const response = await fetch(`http://${host}/__admin/scenarios/reset`, {
44
+ method: "POST",
45
+ });
46
+ // eslint-disable-next-line no-console
47
+ console.log(`Goodmock scenarios reset (status: ${response.status})`);
48
+ }
49
+ /**
50
+ * @internal
51
+ * Clear all goodmock stub mappings.
52
+ */
53
+ export async function resetMappings(host) {
54
+ const response = await fetch(`http://${host}/__admin/reset`, {
55
+ method: "POST",
56
+ });
57
+ // eslint-disable-next-line no-console
58
+ console.log(`Goodmock mappings reset (status: ${response.status})`);
59
+ }
60
+ /**
61
+ * @internal
62
+ * Add a goodmock stub that mocks log requests (POST /gdc/app/projects/.../log).
63
+ * This prevents the app from failing when it tries to send logs.
64
+ */
65
+ export async function mockLogRequests(host) {
66
+ const response = await fetch(`http://${host}/__admin/mappings`, {
67
+ method: "POST",
68
+ headers: { "Content-Type": "application/json" },
69
+ body: JSON.stringify({
70
+ request: {
71
+ method: "POST",
72
+ urlPattern: "/gdc/app/projects/.*/log",
73
+ },
74
+ response: {
75
+ body: "",
76
+ status: 200,
77
+ },
78
+ }),
79
+ });
80
+ // eslint-disable-next-line no-console
81
+ console.log(`Goodmock log requests mocked (status: ${response.status})`);
82
+ }
83
+ export { GoodmockMode };
84
+ /**
85
+ * @internal
86
+ * Which mode goodmock is running in
87
+ */
88
+ var GoodmockMode;
89
+ (function (GoodmockMode) {
90
+ GoodmockMode["Replay"] = "replay";
91
+ GoodmockMode["Record"] = "record";
92
+ GoodmockMode["Proxy"] = "proxy";
93
+ })(GoodmockMode || (GoodmockMode = {}));
94
+ /**
95
+ * @internal
96
+ */
97
+ export function goodmockMode() {
98
+ return process.env["GOODMOCK_MODE"];
99
+ }
100
+ /**
101
+ * @internal
102
+ * Add a catch-all proxy mapping so goodmock forwards every request to the
103
+ * real backend and records the interactions. Must be called after
104
+ * {@link resetMappings} in recording mode.
105
+ */
106
+ export async function startRecording(host, backendHost) {
107
+ const response = await fetch(`http://${host}/__admin/mappings`, {
108
+ method: "POST",
109
+ headers: { "Content-Type": "application/json" },
110
+ body: JSON.stringify({
111
+ request: { method: "ANY", urlPattern: ".*" },
112
+ response: { proxyBaseUrl: backendHost },
113
+ }),
114
+ });
115
+ // eslint-disable-next-line no-console
116
+ console.log(`Goodmock recording started, proxying to ${backendHost} (status: ${response.status})`);
117
+ }
118
+ const snapshotParams = {
119
+ captureHeaders: { "X-GDC-TEST-NAME": {} },
120
+ requestBodyPattern: {
121
+ matcher: "equalToJson",
122
+ ignoreArrayOrder: false,
123
+ ignoreExtraElements: false,
124
+ },
125
+ };
126
+ function sanitizeWorkspaceId(mappings, sourceWorkspaceId, targetWorkspaceId) {
127
+ if (sourceWorkspaceId === targetWorkspaceId) {
128
+ return mappings;
129
+ }
130
+ const dataString = JSON.stringify({ mappings });
131
+ const sanitizedDataString = dataString.split(sourceWorkspaceId).join(targetWorkspaceId);
132
+ const sanitizedData = JSON.parse(sanitizedDataString);
133
+ // eslint-disable-next-line no-console
134
+ console.log(`Sanitized workspaceId in mappings (${sourceWorkspaceId} -> ${targetWorkspaceId})`);
135
+ return sanitizedData.mappings;
136
+ }
137
+ /**
138
+ * @internal
139
+ * Snapshot goodmock recordings, save the combined mappings to a JSON file
140
+ * on disk, and sanitize any credentials from the output.
141
+ *
142
+ * This mirrors the Cypress recording flow: two snapshot calls are made —
143
+ * one for executionResults URLs (with repeatsAsScenarios) and one for
144
+ * everything else — then the results are merged and written out.
145
+ *
146
+ * @param host -
147
+ * @param mappingFilePath -
148
+ * @param workspaceIdMappings - Optional source/target workspace ID rewrite(s)
149
+ * applied before mappings are saved. Accepts a single mapping or an array.
150
+ * @param backendHost - Optional real backend URL (e.g. "https://example.gooddata.com").
151
+ * When provided together with baseUrl, all occurrences are replaced in the
152
+ * saved mappings so that recorded responses (e.g. geo tile URLs) resolve
153
+ * correctly during replay.
154
+ * @param baseUrl - Optional app base URL (e.g. "http://kpi-dashboards-ui:9500")
155
+ * that replaces backendHost references in the saved mappings.
156
+ */
157
+ export async function snapshotAndSaveRecording(host, mappingFilePath, workspaceIdMappings, backendHost, baseUrl) {
158
+ // Snapshot executionResults with scenarios
159
+ const scenariosRes = await fetch(`http://${host}/__admin/recordings/snapshot`, {
160
+ method: "POST",
161
+ headers: { "Content-Type": "application/json" },
162
+ body: JSON.stringify({
163
+ repeatsAsScenarios: true,
164
+ filters: { urlPattern: ".*executionResults.*" },
165
+ persist: false,
166
+ ...snapshotParams,
167
+ }),
168
+ });
169
+ const scenariosData = (await scenariosRes.json());
170
+ // Snapshot everything else without scenarios
171
+ const plainRes = await fetch(`http://${host}/__admin/recordings/snapshot`, {
172
+ method: "POST",
173
+ headers: { "Content-Type": "application/json" },
174
+ body: JSON.stringify({
175
+ repeatsAsScenarios: false,
176
+ filters: { urlPattern: "((?!executionResults).)*" },
177
+ persist: false,
178
+ ...snapshotParams,
179
+ }),
180
+ });
181
+ const plainData = (await plainRes.json());
182
+ const mappings = [...plainData.mappings, ...scenariosData.mappings];
183
+ // Sanitize credentials
184
+ for (const mapping of mappings) {
185
+ if (mapping?.request?.url === "/gdc/account/login") {
186
+ delete mapping.request.bodyPatterns;
187
+ if (mapping.response.headers) {
188
+ delete mapping.response.headers["Set-Cookie"];
189
+ }
190
+ }
191
+ const requestUrl = mapping?.request?.url;
192
+ if (typeof requestUrl === "string" &&
193
+ /^\/api\/v1\/actions\/workspaces\/[^/]+\/resolveSettings$/.test(requestUrl)) {
194
+ try {
195
+ const parsedBody = mapping?.response?.jsonBody;
196
+ if (Array.isArray(parsedBody)) {
197
+ for (const setting of parsedBody) {
198
+ if ((setting?.id === "agGridToken" || setting?.id === "mapboxToken") &&
199
+ setting?.content &&
200
+ typeof setting.content === "object") {
201
+ setting.content.value = "";
202
+ }
203
+ }
204
+ mapping.response.jsonBody = parsedBody;
205
+ }
206
+ }
207
+ catch {
208
+ console.warn(`sanitizeCredentials – resolveSettings body is not valid JSON for url: ${requestUrl}`);
209
+ }
210
+ }
211
+ }
212
+ const mappingsToApply = Array.isArray(workspaceIdMappings)
213
+ ? workspaceIdMappings
214
+ : workspaceIdMappings
215
+ ? [workspaceIdMappings]
216
+ : [];
217
+ const sanitizedMappings = mappingsToApply.reduce((acc, { sourceWorkspaceId, targetWorkspaceId }) => sanitizeWorkspaceId(acc, sourceWorkspaceId, targetWorkspaceId), mappings);
218
+ let output = JSON.stringify({ mappings: sanitizedMappings }, null, 4) + "\n";
219
+ if (backendHost && baseUrl) {
220
+ output = output.replaceAll(backendHost, baseUrl);
221
+ }
222
+ mkdirSync(dirname(mappingFilePath), { recursive: true });
223
+ writeFileSync(mappingFilePath, output);
224
+ // eslint-disable-next-line no-console
225
+ console.log(`Recording saved to ${mappingFilePath} (${sanitizedMappings.length} mappings)`);
226
+ }
227
+ //# sourceMappingURL=goodmock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goodmock.js","sourceRoot":"","sources":["../src/goodmock.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAEhC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,eAAuB,EAAiB;IACrF,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACD,QAAQ,GAAG,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,CAAC,IAAI,CAAC,0BAA0B,eAAe,YAAY,CAAC,CAAC;QACpE,OAAO;IACX,CAAC;IAED,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACD,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,CAAC,KAAK,CAAC,iCAAiC,eAAe,EAAE,CAAC,CAAC;QAClE,OAAO;IACX,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,UAAU,IAAI,0BAA0B,EAAE;QACnE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC7B,CAAC,CAAC;IAEH,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,iCAAiC,eAAe,aAAa,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;AAAA,CAChG;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY,EAAiB;IAC9D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,UAAU,IAAI,0BAA0B,EAAE;QACnE,MAAM,EAAE,MAAM;KACjB,CAAC,CAAC;IACH,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,qCAAqC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;AAAA,CACxE;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY,EAAiB;IAC7D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,UAAU,IAAI,gBAAgB,EAAE;QACzD,MAAM,EAAE,MAAM;KACjB,CAAC,CAAC;IACH,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,oCAAoC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;AAAA,CACvE;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY,EAAiB;IAC/D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,UAAU,IAAI,mBAAmB,EAAE;QAC5D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACjB,OAAO,EAAE;gBACL,MAAM,EAAE,MAAM;gBACd,UAAU,EAAE,0BAA0B;aACzC;YACD,QAAQ,EAAE;gBACN,IAAI,EAAE,EAAE;gBACR,MAAM,EAAE,GAAG;aACd;SACJ,CAAC;KACL,CAAC,CAAC;IACH,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,yCAAyC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;AAAA,CAC5E;SAMW,YAAY;AAJxB;;;GAGG;AACH,IAAY,YAIX;AAJD,WAAY,YAAY;IACpB,iCAAiB,CAAA;IACjB,iCAAiB,CAAA;IACjB,+BAAe,CAAA;AAAC,CACpB,EAJY,YAAY,KAAZ,YAAY,QAIvB;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,GAAiB;IACzC,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAiB,CAAC;AAAA,CACvD;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,WAAmB,EAAiB;IACnF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,UAAU,IAAI,mBAAmB,EAAE;QAC5D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACjB,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE;YAC5C,QAAQ,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE;SAC1C,CAAC;KACL,CAAC,CAAC;IACH,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,2CAA2C,WAAW,aAAa,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;AAAA,CACtG;AAED,MAAM,cAAc,GAAG;IACnB,cAAc,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE;IACzC,kBAAkB,EAAE;QAChB,OAAO,EAAE,aAAa;QACtB,gBAAgB,EAAE,KAAK;QACvB,mBAAmB,EAAE,KAAK;KAC7B;CACJ,CAAC;AAeF,SAAS,mBAAmB,CACxB,QAA4B,EAC5B,iBAAyB,EACzB,iBAAyB,EACP;IAClB,IAAI,iBAAiB,KAAK,iBAAiB,EAAE,CAAC;QAC1C,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;IAChD,MAAM,mBAAmB,GAAG,UAAU,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACxF,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAqC,CAAC;IAE1F,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,sCAAsC,iBAAiB,OAAO,iBAAiB,GAAG,CAAC,CAAC;IAEhG,OAAO,aAAa,CAAC,QAAQ,CAAC;AAAA,CACjC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC1C,IAAY,EACZ,eAAuB,EACvB,mBAAiE,EACjE,WAAoB,EACpB,OAAgB,EACH;IACb,2CAA2C;IAC3C,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,UAAU,IAAI,8BAA8B,EAAE;QAC3E,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACjB,kBAAkB,EAAE,IAAI;YACxB,OAAO,EAAE,EAAE,UAAU,EAAE,sBAAsB,EAAE;YAC/C,OAAO,EAAE,KAAK;YACd,GAAG,cAAc;SACpB,CAAC;KACL,CAAC,CAAC;IACH,MAAM,aAAa,GAAG,CAAC,MAAM,YAAY,CAAC,IAAI,EAAE,CAAqC,CAAC;IAEtF,6CAA6C;IAC7C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,UAAU,IAAI,8BAA8B,EAAE;QACvE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACjB,kBAAkB,EAAE,KAAK;YACzB,OAAO,EAAE,EAAE,UAAU,EAAE,0BAA0B,EAAE;YACnD,OAAO,EAAE,KAAK;YACd,GAAG,cAAc;SACpB,CAAC;KACL,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAqC,CAAC;IAE9E,MAAM,QAAQ,GAAG,CAAC,GAAG,SAAS,CAAC,QAAQ,EAAE,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAEpE,uBAAuB;IACvB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC7B,IAAI,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,oBAAoB,EAAE,CAAC;YACjD,OAAO,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC;YACpC,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;gBAC3B,OAAO,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAClD,CAAC;QACL,CAAC;QAED,MAAM,UAAU,GAAG,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC;QACzC,IACI,OAAO,UAAU,KAAK,QAAQ;YAC9B,0DAA0D,CAAC,IAAI,CAAC,UAAU,CAAC,EAC7E,CAAC;YACC,IAAI,CAAC;gBACD,MAAM,UAAU,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC;gBAC/C,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC5B,KAAK,MAAM,OAAO,IAAI,UAAU,EAAE,CAAC;wBAC/B,IACI,CAAC,OAAO,EAAE,EAAE,KAAK,aAAa,IAAI,OAAO,EAAE,EAAE,KAAK,aAAa,CAAC;4BAChE,OAAO,EAAE,OAAO;4BAChB,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,EACrC,CAAC;4BACC,OAAO,CAAC,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC;wBAC/B,CAAC;oBACL,CAAC;oBACD,OAAO,CAAC,QAAQ,CAAC,QAAQ,GAAG,UAAU,CAAC;gBAC3C,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACL,OAAO,CAAC,IAAI,CACR,2EAAyE,UAAU,EAAE,CACxF,CAAC;YACN,CAAC;QACL,CAAC;IACL,CAAC;IAED,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAC;QACtD,CAAC,CAAC,mBAAmB;QACrB,CAAC,CAAC,mBAAmB;YACnB,CAAC,CAAC,CAAC,mBAAmB,CAAC;YACvB,CAAC,CAAC,EAAE,CAAC;IACX,MAAM,iBAAiB,GAAG,eAAe,CAAC,MAAM,CAC5C,CAAC,GAAG,EAAE,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,EAAE,EAAE,CAC9C,mBAAmB,CAAC,GAAG,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,EAClE,QAAQ,CACX,CAAC;IAEF,IAAI,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7E,IAAI,WAAW,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC;IAED,SAAS,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,aAAa,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IACvC,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,sBAAsB,eAAe,KAAK,iBAAiB,CAAC,MAAM,YAAY,CAAC,CAAC;AAAA,CAC/F"}
@@ -0,0 +1,21 @@
1
+ import { type Locator, type Page } from "@playwright/test";
2
+ /**
3
+ * @internal
4
+ * Click at the center of the bounding box of the given locator.
5
+ * Throws if the element is not visible (no bounding box).
6
+ * Allows to use mouse up and down instead of click for some fragile cases.
7
+ */
8
+ export declare function clickByBoundingBox(page: Page, locator: Locator, options?: {
9
+ useMouseUpDown?: boolean;
10
+ steps?: number;
11
+ timeout?: number;
12
+ }): Promise<void>;
13
+ /**
14
+ * @internal
15
+ * Hover (move mouse) to the center of the bounding box of the given locator.
16
+ * Throws if the element is not visible (no bounding box).
17
+ */
18
+ export declare function hoverByBoundingBox(page: Page, locator: Locator, options?: {
19
+ steps?: number;
20
+ }): Promise<void>;
21
+ //# sourceMappingURL=mouse-actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mouse-actions.d.ts","sourceRoot":"","sources":["../../src/helpers/mouse-actions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE3D;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACpC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACzE,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACpC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,OAAO,CAAC,IAAI,CAAC,CAIf"}
@@ -0,0 +1,35 @@
1
+ // (C) 2026 GoodData Corporation
2
+ /**
3
+ * @internal
4
+ * Click at the center of the bounding box of the given locator.
5
+ * Throws if the element is not visible (no bounding box).
6
+ * Allows to use mouse up and down instead of click for some fragile cases.
7
+ */
8
+ export async function clickByBoundingBox(page, locator, options) {
9
+ const box = await locator.boundingBox();
10
+ if (!box)
11
+ throw new Error("Element not visible");
12
+ if (options?.useMouseUpDown) {
13
+ const steps = options?.steps ?? 5;
14
+ const timeout = options?.timeout ?? 0;
15
+ await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2, { steps });
16
+ await page.mouse.down();
17
+ await page.waitForTimeout(timeout);
18
+ await page.mouse.up();
19
+ }
20
+ else {
21
+ await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
22
+ }
23
+ }
24
+ /**
25
+ * @internal
26
+ * Hover (move mouse) to the center of the bounding box of the given locator.
27
+ * Throws if the element is not visible (no bounding box).
28
+ */
29
+ export async function hoverByBoundingBox(page, locator, options) {
30
+ const box = await locator.boundingBox();
31
+ if (!box)
32
+ throw new Error("Element not visible");
33
+ await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2, options);
34
+ }
35
+ //# sourceMappingURL=mouse-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mouse-actions.js","sourceRoot":"","sources":["../../src/helpers/mouse-actions.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAIhC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACpC,IAAU,EACV,OAAgB,EAChB,OAAwE,EAC3D;IACb,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;IACxC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAEjD,IAAI,OAAO,EAAE,cAAc,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,CAAC,CAAC;QACtC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAChF,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACxB,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QACnC,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;IAC1B,CAAC;SAAM,CAAC;QACJ,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC1E,CAAC;AAAA,CACJ;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACpC,IAAU,EACV,OAAgB,EAChB,OAA4B,EACf;IACb,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;IACxC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACjD,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;AAAA,CACjF"}
package/esm/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export { authHeader, injectAuthHeader } from "./auth.js";
2
+ export { type IWorkspaceIdMapping, loadMappings, resetMappings, resetScenarios, mockLogRequests, goodmockMode, GoodmockMode, startRecording, snapshotAndSaveRecording, } from "./goodmock.js";
3
+ export { type BaseTestArgs, type BaseWorkerArgs, type ICreateTestOptions, type IDescribeFunction, type IE2eTest, type IE2eTestDetails, type IFeatureHubEnvironment, type IFeatureHubFeature, type IGoodmockOptions, createTest, } from "./playwright.js";
4
+ export { clickByBoundingBox, hoverByBoundingBox } from "./helpers/mouse-actions.js";
5
+ export { GOODMOCK_HOST, API_TOKEN, BACKEND_HOST, getBaseUrl, getEnvWithFallback, getWorkspaceId, } from "./constants.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EACH,KAAK,mBAAmB,EACxB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,wBAAwB,GAC3B,MAAM,eAAe,CAAC;AACvB,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,UAAU,GACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,EACH,aAAa,EACb,SAAS,EACT,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,cAAc,GACjB,MAAM,gBAAgB,CAAC"}
package/esm/index.js ADDED
@@ -0,0 +1,8 @@
1
+ // (C) 2026 GoodData Corporation
2
+ /* oxlint-disable no-barrel-files/no-barrel-files */
3
+ export { authHeader, injectAuthHeader } from "./auth.js";
4
+ export { loadMappings, resetMappings, resetScenarios, mockLogRequests, goodmockMode, GoodmockMode, startRecording, snapshotAndSaveRecording, } from "./goodmock.js";
5
+ export { createTest, } from "./playwright.js";
6
+ export { clickByBoundingBox, hoverByBoundingBox } from "./helpers/mouse-actions.js";
7
+ export { GOODMOCK_HOST, API_TOKEN, BACKEND_HOST, getBaseUrl, getEnvWithFallback, getWorkspaceId, } from "./constants.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAEhC,oDAAoD;AAEpD,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAEH,YAAY,EACZ,aAAa,EACb,cAAc,EACd,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,wBAAwB,GAC3B,MAAM,eAAe,CAAC;AACvB,OAAO,EAUH,UAAU,GACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,EACH,aAAa,EACb,SAAS,EACT,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,cAAc,GACjB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,91 @@
1
+ import { type Fixtures, type PlaywrightTestArgs, type PlaywrightTestOptions, type PlaywrightWorkerArgs, type PlaywrightWorkerOptions, type TestDetails, test } from "@playwright/test";
2
+ import { type IWorkspaceIdMapping } from "./goodmock.js";
3
+ /**
4
+ * @internal
5
+ */
6
+ export interface IGoodmockOptions {
7
+ host: string;
8
+ backendHost: string;
9
+ getMappingPath: (specFile: string) => string;
10
+ workspaceIdMappings?: IWorkspaceIdMapping | IWorkspaceIdMapping[];
11
+ baseUrl?: string;
12
+ }
13
+ /**
14
+ * @internal
15
+ */
16
+ export interface IE2eTestDetails extends TestDetails {
17
+ workspaceSettings?: Record<string, unknown>;
18
+ additionalWindowProperties?: Record<string, unknown>;
19
+ }
20
+ /**
21
+ * @internal
22
+ */
23
+ export interface IFeatureHubFeature {
24
+ id: string;
25
+ key: string;
26
+ l: boolean;
27
+ version?: number;
28
+ type: string;
29
+ value?: boolean | string | number;
30
+ strategies?: unknown[];
31
+ v?: string;
32
+ }
33
+ /**
34
+ * @internal
35
+ */
36
+ export interface IFeatureHubEnvironment {
37
+ id: string;
38
+ features: IFeatureHubFeature[];
39
+ }
40
+ /**
41
+ * @internal
42
+ */
43
+ export type BaseTestArgs = PlaywrightTestArgs & PlaywrightTestOptions;
44
+ /**
45
+ * @internal
46
+ */
47
+ export type BaseWorkerArgs = PlaywrightWorkerArgs & PlaywrightWorkerOptions;
48
+ /**
49
+ * @internal
50
+ */
51
+ export interface ICreateTestOptions<T extends Record<string, unknown> = {}, W extends Record<string, unknown> = {}> {
52
+ goodmock?: IGoodmockOptions;
53
+ fixtures?: Fixtures<T, W, BaseTestArgs & T, BaseWorkerArgs & W>;
54
+ featureHubResponse?: IFeatureHubEnvironment[];
55
+ }
56
+ /**
57
+ * @internal
58
+ */
59
+ export interface IDescribeFunction {
60
+ (suiteName: string, specName: string, fn: () => void): void;
61
+ (suiteName: string, specName: string, details: IE2eTestDetails, fn: () => void): void;
62
+ skip: {
63
+ (suiteName: string, specName: string, fn: () => void): void;
64
+ (suiteName: string, specName: string, details: IE2eTestDetails, fn: () => void): void;
65
+ };
66
+ }
67
+ /**
68
+ * @internal
69
+ */
70
+ export type IE2eTest = typeof test & {
71
+ topLevelDescribe: IDescribeFunction;
72
+ describe: {
73
+ (title: string, callback: () => void): void;
74
+ (title: string, details: IE2eTestDetails, callback: () => void): void;
75
+ skip: {
76
+ (title: string, callback: () => void): void;
77
+ (title: string, details: IE2eTestDetails, callback: () => void): void;
78
+ };
79
+ only: (typeof test)["describe"]["only"];
80
+ configure: (typeof test)["describe"]["configure"];
81
+ fixme: (typeof test)["describe"]["fixme"];
82
+ serial: (typeof test)["describe"]["serial"];
83
+ parallel: (typeof test)["describe"]["parallel"];
84
+ };
85
+ };
86
+ /**
87
+ * @internal
88
+ * @param options -
89
+ */
90
+ export declare function createTest<T extends Record<string, unknown> = {}, W extends Record<string, unknown> = {}>(options?: ICreateTestOptions<T, W>): IE2eTest;
91
+ //# sourceMappingURL=playwright.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../src/playwright.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,182 @@
1
+ // (C) 2026 GoodData Corporation
2
+ // oxlint-disable typescript-eslint/no-empty-object-type
3
+ import { test, } from "@playwright/test";
4
+ import { GoodmockMode, goodmockMode as getGoodmockMode, loadMappings, resetMappings, snapshotAndSaveRecording, startRecording, } from "./goodmock.js";
5
+ // --- Internal helpers ---
6
+ function parseArgs(detailsOrFn, func) {
7
+ const hasDetails = typeof detailsOrFn !== "function";
8
+ return {
9
+ details: hasDetails ? detailsOrFn : undefined,
10
+ fn: hasDetails ? func : detailsOrFn,
11
+ };
12
+ }
13
+ /** Strip custom fields before passing details to Playwright. */
14
+ function toPlaywrightDetails(details) {
15
+ if (!details)
16
+ return undefined;
17
+ // oxlint-disable-next-line @typescript-eslint/no-unused-vars
18
+ const { workspaceSettings: _ws, additionalWindowProperties: _awp, ...rest } = details;
19
+ return Object.keys(rest).length > 0 ? rest : undefined;
20
+ }
21
+ /** Inject workspace settings and additional window properties via a single addInitScript. */
22
+ function injectWindowProperties(testInst, settings, awp) {
23
+ testInst.beforeEach(async ({ page }) => {
24
+ await page.addInitScript((args) => {
25
+ const w = window;
26
+ if (Object.keys(args.s).length > 0) {
27
+ w["customWorkspaceSettings"] = args.s;
28
+ }
29
+ for (const [key, value] of Object.entries(args.a)) {
30
+ w[key] = value;
31
+ }
32
+ }, { s: settings, a: awp });
33
+ });
34
+ }
35
+ // --- Factory ---
36
+ /**
37
+ * @internal
38
+ * @param options -
39
+ */
40
+ export function createTest(options = {}) {
41
+ const testInstance = options.fixtures
42
+ ? test.extend(options.fixtures)
43
+ : test;
44
+ // Tracks whether we're inside a topLevelDescribe during synchronous registration.
45
+ let insideTopLevelDescribe = false;
46
+ // Stacks track workspace settings / additionalWindowProperties hierarchy during
47
+ // synchronous describe registration. Each level merges on top of the parent's.
48
+ // The beforeEach at each level captures the fully-merged snapshot, so the
49
+ // innermost beforeEach (which runs last) wins.
50
+ const settingsStack = [];
51
+ const awpStack = [];
52
+ function currentSettings() {
53
+ return settingsStack.length > 0 ? settingsStack[settingsStack.length - 1] : {};
54
+ }
55
+ function currentAwp() {
56
+ return awpStack.length > 0 ? awpStack[awpStack.length - 1] : {};
57
+ }
58
+ /** Push merged settings/awp onto stacks, register a single beforeEach, call fn, pop. */
59
+ function withDescribeDetails(ws, awp, fn) {
60
+ if (!ws && !awp) {
61
+ fn();
62
+ return;
63
+ }
64
+ const mergedSettings = ws ? { ...currentSettings(), ...ws } : currentSettings();
65
+ const mergedAwp = awp ? { ...currentAwp(), ...awp } : currentAwp();
66
+ if (ws)
67
+ settingsStack.push(mergedSettings);
68
+ if (awp)
69
+ awpStack.push(mergedAwp);
70
+ injectWindowProperties(testInstance, mergedSettings, mergedAwp);
71
+ fn();
72
+ if (awp)
73
+ awpStack.pop();
74
+ if (ws)
75
+ settingsStack.pop();
76
+ }
77
+ function buildSuiteCallback(specName, details, fn) {
78
+ return () => {
79
+ // FeatureHub mock — each test gets a fresh page, so route dies with it.
80
+ if (options.featureHubResponse) {
81
+ const body = JSON.stringify(options.featureHubResponse);
82
+ testInstance.beforeEach(async ({ page }) => {
83
+ await page.route("**/features*", async (route) => {
84
+ await route.fulfill({
85
+ status: 200,
86
+ contentType: "application/json",
87
+ body,
88
+ });
89
+ });
90
+ });
91
+ }
92
+ // Goodmock lifecycle hooks
93
+ const gm = options.goodmock;
94
+ if (gm?.host) {
95
+ const goodmockMode = getGoodmockMode();
96
+ if (goodmockMode !== GoodmockMode.Proxy) {
97
+ testInstance.beforeAll(async () => {
98
+ await resetMappings(gm.host);
99
+ if (goodmockMode === GoodmockMode.Record) {
100
+ await startRecording(gm.host, gm.backendHost);
101
+ }
102
+ else if (goodmockMode === GoodmockMode.Replay) {
103
+ await loadMappings(gm.host, gm.getMappingPath(specName));
104
+ }
105
+ });
106
+ testInstance.afterAll(async () => {
107
+ if (goodmockMode === GoodmockMode.Record) {
108
+ await snapshotAndSaveRecording(gm.host, gm.getMappingPath(specName), gm.workspaceIdMappings, gm.backendHost, gm.baseUrl);
109
+ }
110
+ await resetMappings(gm.host);
111
+ });
112
+ // TODO: When this is supported in goodmock, uncomment this block
113
+ // testInstance.beforeEach(async () => {
114
+ // await resetScenarios(gm.host);
115
+ // });
116
+ }
117
+ }
118
+ // Workspace settings + additional window properties — merge with parent
119
+ // stack and inject beforeEach. Wraps fn() so nested describes inherit.
120
+ insideTopLevelDescribe = true;
121
+ withDescribeDetails(details?.workspaceSettings, details?.additionalWindowProperties, fn);
122
+ insideTopLevelDescribe = false;
123
+ };
124
+ }
125
+ // --- Override test.describe to support workspaceSettings merging ---
126
+ const originalDescribe = testInstance.describe;
127
+ function wrappedDescribe(title, detailsOrCb, cb) {
128
+ if (!insideTopLevelDescribe) {
129
+ throw new Error(`test.describe("${title}") must be nested inside a test.topLevelDescribe() block.`);
130
+ }
131
+ const hasDetails = typeof detailsOrCb !== "function";
132
+ const details = hasDetails ? detailsOrCb : undefined;
133
+ const callback = hasDetails ? cb : detailsOrCb;
134
+ const pwDetails = toPlaywrightDetails(details);
135
+ const wrapped = () => withDescribeDetails(details?.workspaceSettings, details?.additionalWindowProperties, callback);
136
+ if (pwDetails) {
137
+ originalDescribe(title, pwDetails, wrapped);
138
+ }
139
+ else {
140
+ originalDescribe(title, wrapped);
141
+ }
142
+ }
143
+ // Wrap describe.skip with the same workspaceSettings merging logic.
144
+ const originalSkip = originalDescribe.skip;
145
+ function wrappedSkip(title, detailsOrCb, cb) {
146
+ const hasDetails = typeof detailsOrCb !== "function";
147
+ const details = hasDetails ? detailsOrCb : undefined;
148
+ const callback = hasDetails ? cb : detailsOrCb;
149
+ const wrapped = () => withDescribeDetails(details?.workspaceSettings, details?.additionalWindowProperties, callback);
150
+ originalSkip(title, wrapped);
151
+ }
152
+ // Preserve all sub-methods (.only, .serial, .parallel, .configure, .fixme)
153
+ Object.assign(wrappedDescribe, originalDescribe);
154
+ wrappedDescribe.skip = wrappedSkip;
155
+ // oxlint-disable-next-line @typescript-eslint/no-explicit-any
156
+ testInstance.describe = wrappedDescribe;
157
+ function topLevelDescribe(suiteName, specName, detailsOrFn, func) {
158
+ const { details, fn } = parseArgs(detailsOrFn, func);
159
+ const suite = buildSuiteCallback(specName, details, fn);
160
+ const pwDetails = toPlaywrightDetails(details);
161
+ if (pwDetails) {
162
+ // oxlint-disable-next-line playwright/valid-describe-callback
163
+ originalDescribe(suiteName, pwDetails, suite);
164
+ }
165
+ else {
166
+ // oxlint-disable-next-line playwright/valid-describe-callback
167
+ originalDescribe(suiteName, suite);
168
+ }
169
+ }
170
+ function skip(suiteName, specName, detailsOrFn, func) {
171
+ const { details, fn } = parseArgs(detailsOrFn, func);
172
+ const suite = buildSuiteCallback(specName, details, fn);
173
+ // oxlint-disable-next-line playwright/valid-describe-callback
174
+ originalSkip(suiteName, suite);
175
+ }
176
+ topLevelDescribe.skip = skip;
177
+ // Attach topLevelDescribe onto the test instance
178
+ const e2eTest = testInstance;
179
+ e2eTest.topLevelDescribe = topLevelDescribe;
180
+ return e2eTest;
181
+ }
182
+ //# sourceMappingURL=playwright.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright.js","sourceRoot":"","sources":["../src/playwright.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,256 @@
1
+ import { Fixtures } from '@playwright/test';
2
+ import { Locator } from '@playwright/test';
3
+ import { Page } from '@playwright/test';
4
+ import { PlaywrightTestArgs } from '@playwright/test';
5
+ import { PlaywrightTestOptions } from '@playwright/test';
6
+ import { PlaywrightWorkerArgs } from '@playwright/test';
7
+ import { PlaywrightWorkerOptions } from '@playwright/test';
8
+ import { test } from '@playwright/test';
9
+ import { TestDetails } from '@playwright/test';
10
+
11
+ /**
12
+ * @internal
13
+ */
14
+ export declare const API_TOKEN: string;
15
+
16
+ /**
17
+ * @internal
18
+ */
19
+ export declare const authHeader: (token: string) => {
20
+ authorization: string;
21
+ };
22
+
23
+ /**
24
+ * @internal
25
+ */
26
+ export declare const BACKEND_HOST: string;
27
+
28
+ /**
29
+ * @internal
30
+ */
31
+ export declare type BaseTestArgs = PlaywrightTestArgs & PlaywrightTestOptions;
32
+
33
+ /**
34
+ * @internal
35
+ */
36
+ export declare type BaseWorkerArgs = PlaywrightWorkerArgs & PlaywrightWorkerOptions;
37
+
38
+ /**
39
+ * @internal
40
+ * Click at the center of the bounding box of the given locator.
41
+ * Throws if the element is not visible (no bounding box).
42
+ * Allows to use mouse up and down instead of click for some fragile cases.
43
+ */
44
+ export declare function clickByBoundingBox(page: Page, locator: Locator, options?: {
45
+ useMouseUpDown?: boolean;
46
+ steps?: number;
47
+ timeout?: number;
48
+ }): Promise<void>;
49
+
50
+ /**
51
+ * @internal
52
+ * @param options -
53
+ */
54
+ export declare function createTest<T extends Record<string, unknown> = {}, W extends Record<string, unknown> = {}>(options?: ICreateTestOptions<T, W>): IE2eTest;
55
+
56
+ /**
57
+ * @internal
58
+ */
59
+ export declare const getBaseUrl: (defaultValue?: string) => string;
60
+
61
+ /**
62
+ * @internal
63
+ */
64
+ export declare const getEnvWithFallback: (env: string, fallback?: string) => string;
65
+
66
+ /**
67
+ * @internal
68
+ */
69
+ export declare const getWorkspaceId: (defaultValue?: string) => string;
70
+
71
+ /**
72
+ * @internal
73
+ */
74
+ export declare const GOODMOCK_HOST: string;
75
+
76
+ /**
77
+ * @internal
78
+ * Which mode goodmock is running in
79
+ */
80
+ export declare enum GoodmockMode {
81
+ Replay = "replay",
82
+ Record = "record",
83
+ Proxy = "proxy"
84
+ }
85
+
86
+ /**
87
+ * @internal
88
+ */
89
+ export declare function goodmockMode(): GoodmockMode;
90
+
91
+ /**
92
+ * @internal
93
+ * Hover (move mouse) to the center of the bounding box of the given locator.
94
+ * Throws if the element is not visible (no bounding box).
95
+ */
96
+ export declare function hoverByBoundingBox(page: Page, locator: Locator, options?: {
97
+ steps?: number;
98
+ }): Promise<void>;
99
+
100
+ /**
101
+ * @internal
102
+ */
103
+ export declare interface ICreateTestOptions<T extends Record<string, unknown> = {}, W extends Record<string, unknown> = {}> {
104
+ goodmock?: IGoodmockOptions;
105
+ fixtures?: Fixtures<T, W, BaseTestArgs & T, BaseWorkerArgs & W>;
106
+ featureHubResponse?: IFeatureHubEnvironment[];
107
+ }
108
+
109
+ /**
110
+ * @internal
111
+ */
112
+ export declare interface IDescribeFunction {
113
+ (suiteName: string, specName: string, fn: () => void): void;
114
+ (suiteName: string, specName: string, details: IE2eTestDetails, fn: () => void): void;
115
+ skip: {
116
+ (suiteName: string, specName: string, fn: () => void): void;
117
+ (suiteName: string, specName: string, details: IE2eTestDetails, fn: () => void): void;
118
+ };
119
+ }
120
+
121
+ /**
122
+ * @internal
123
+ */
124
+ export declare type IE2eTest = typeof test & {
125
+ topLevelDescribe: IDescribeFunction;
126
+ describe: {
127
+ (title: string, callback: () => void): void;
128
+ (title: string, details: IE2eTestDetails, callback: () => void): void;
129
+ skip: {
130
+ (title: string, callback: () => void): void;
131
+ (title: string, details: IE2eTestDetails, callback: () => void): void;
132
+ };
133
+ only: (typeof test)["describe"]["only"];
134
+ configure: (typeof test)["describe"]["configure"];
135
+ fixme: (typeof test)["describe"]["fixme"];
136
+ serial: (typeof test)["describe"]["serial"];
137
+ parallel: (typeof test)["describe"]["parallel"];
138
+ };
139
+ };
140
+
141
+ /**
142
+ * @internal
143
+ */
144
+ export declare interface IE2eTestDetails extends TestDetails {
145
+ workspaceSettings?: Record<string, unknown>;
146
+ additionalWindowProperties?: Record<string, unknown>;
147
+ }
148
+
149
+ /**
150
+ * @internal
151
+ */
152
+ export declare interface IFeatureHubEnvironment {
153
+ id: string;
154
+ features: IFeatureHubFeature[];
155
+ }
156
+
157
+ /**
158
+ * @internal
159
+ */
160
+ export declare interface IFeatureHubFeature {
161
+ id: string;
162
+ key: string;
163
+ l: boolean;
164
+ version?: number;
165
+ type: string;
166
+ value?: boolean | string | number;
167
+ strategies?: unknown[];
168
+ v?: string;
169
+ }
170
+
171
+ /**
172
+ * @internal
173
+ */
174
+ export declare interface IGoodmockOptions {
175
+ host: string;
176
+ backendHost: string;
177
+ getMappingPath: (specFile: string) => string;
178
+ workspaceIdMappings?: IWorkspaceIdMapping | IWorkspaceIdMapping[];
179
+ baseUrl?: string;
180
+ }
181
+
182
+ /**
183
+ * @internal
184
+ * Injects Authorization bearer token into all /api/ requests via Playwright route interception.
185
+ */
186
+ export declare function injectAuthHeader(page: Page, token: string): Promise<void>;
187
+
188
+ /**
189
+ * @internal
190
+ */
191
+ export declare interface IWorkspaceIdMapping {
192
+ sourceWorkspaceId: string;
193
+ targetWorkspaceId: string;
194
+ }
195
+
196
+ /**
197
+ * @internal
198
+ * Load goodmock stub mappings from a JSON file on disk.
199
+ *
200
+ * @param host - Goodmock host:port (e.g. "backend-mock:8080")
201
+ * @param mappingFilePath - Absolute path to the mapping JSON file
202
+ */
203
+ export declare function loadMappings(host: string, mappingFilePath: string): Promise<void>;
204
+
205
+ /**
206
+ * @internal
207
+ * Add a goodmock stub that mocks log requests (POST /gdc/app/projects/.../log).
208
+ * This prevents the app from failing when it tries to send logs.
209
+ */
210
+ export declare function mockLogRequests(host: string): Promise<void>;
211
+
212
+ /**
213
+ * @internal
214
+ * Clear all goodmock stub mappings.
215
+ */
216
+ export declare function resetMappings(host: string): Promise<void>;
217
+
218
+ /**
219
+ * @internal
220
+ * Reset goodmock scenario state (sequence counters) without clearing mappings.
221
+ * Must be called between tests so scenario-driven stubs start from the
222
+ * beginning for every test — mirrors the Cypress `resetRecordingsScenarios` task.
223
+ */
224
+ export declare function resetScenarios(host: string): Promise<void>;
225
+
226
+ /**
227
+ * @internal
228
+ * Snapshot goodmock recordings, save the combined mappings to a JSON file
229
+ * on disk, and sanitize any credentials from the output.
230
+ *
231
+ * This mirrors the Cypress recording flow: two snapshot calls are made —
232
+ * one for executionResults URLs (with repeatsAsScenarios) and one for
233
+ * everything else — then the results are merged and written out.
234
+ *
235
+ * @param host -
236
+ * @param mappingFilePath -
237
+ * @param workspaceIdMappings - Optional source/target workspace ID rewrite(s)
238
+ * applied before mappings are saved. Accepts a single mapping or an array.
239
+ * @param backendHost - Optional real backend URL (e.g. "https://example.gooddata.com").
240
+ * When provided together with baseUrl, all occurrences are replaced in the
241
+ * saved mappings so that recorded responses (e.g. geo tile URLs) resolve
242
+ * correctly during replay.
243
+ * @param baseUrl - Optional app base URL (e.g. "http://kpi-dashboards-ui:9500")
244
+ * that replaces backendHost references in the saved mappings.
245
+ */
246
+ export declare function snapshotAndSaveRecording(host: string, mappingFilePath: string, workspaceIdMappings?: IWorkspaceIdMapping | IWorkspaceIdMapping[], backendHost?: string, baseUrl?: string): Promise<void>;
247
+
248
+ /**
249
+ * @internal
250
+ * Add a catch-all proxy mapping so goodmock forwards every request to the
251
+ * real backend and records the interactions. Must be called after
252
+ * {@link resetMappings} in recording mode.
253
+ */
254
+ export declare function startRecording(host: string, backendHost: string): Promise<void>;
255
+
256
+ export { }
@@ -0,0 +1,11 @@
1
+ // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2
+ // It should be published with your NPM package. It should not be tracked by Git.
3
+ {
4
+ "tsdocVersion": "0.12",
5
+ "toolPackages": [
6
+ {
7
+ "packageName": "@microsoft/api-extractor",
8
+ "packageVersion": "7.55.2"
9
+ }
10
+ ]
11
+ }
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@gooddata/sdk-e2e-utils",
3
+ "version": "11.31.0-alpha.6",
4
+ "description": "GoodData utility functions for Playwright E2E tests",
5
+ "license": "MIT",
6
+ "author": "GoodData",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/gooddata/gooddata-ui-sdk.git",
10
+ "directory": "libs/sdk-e2e-utils"
11
+ },
12
+ "files": [
13
+ "esm"
14
+ ],
15
+ "type": "module",
16
+ "main": "./esm/index.js",
17
+ "types": "./esm/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./esm/index.d.ts",
21
+ "import": "./esm/index.js",
22
+ "default": "./esm/index.js"
23
+ }
24
+ },
25
+ "dependencies": {
26
+ "@playwright/test": "1.59.1"
27
+ },
28
+ "devDependencies": {
29
+ "@microsoft/api-documenter": "^7.17.0",
30
+ "@microsoft/api-extractor": "^7.55.2",
31
+ "@types/node": "24.12.0",
32
+ "@typescript-eslint/eslint-plugin": "8.58.0",
33
+ "@typescript-eslint/parser": "8.58.0",
34
+ "@typescript/native-preview": "7.0.0-dev.20260202.1",
35
+ "eslint": "^9.39.2",
36
+ "eslint-import-resolver-typescript": "4.4.4",
37
+ "eslint-plugin-chai-friendly": "1.1.0",
38
+ "eslint-plugin-headers": "1.3.3",
39
+ "eslint-plugin-import-esm": "1.2.1",
40
+ "eslint-plugin-import-x": "4.16.1",
41
+ "eslint-plugin-jsdoc": "62.1.0",
42
+ "eslint-plugin-no-barrel-files": "1.2.2",
43
+ "eslint-plugin-no-only-tests": "3.3.0",
44
+ "eslint-plugin-playwright": "2.10.1",
45
+ "eslint-plugin-regexp": "1.15.0",
46
+ "eslint-plugin-sonarjs": "3.0.6",
47
+ "eslint-plugin-tsdoc": "0.2.14",
48
+ "jiti": "2.6.1",
49
+ "npm-run-all": "^4.1.5",
50
+ "oxfmt": "0.45.0",
51
+ "oxlint": "^1.43.0",
52
+ "oxlint-tsgolint": "0.11.4",
53
+ "typescript": "5.9.3",
54
+ "@gooddata/oxlint-config": "11.31.0-alpha.6",
55
+ "@gooddata/eslint-config": "11.31.0-alpha.6"
56
+ },
57
+ "scripts": {
58
+ "_phase:build": "npm run build",
59
+ "_phase:validate": "npm run validate",
60
+ "api-extractor": "mkdir -p api && [ -z \"${CI}\" ] && (api-extractor run -l) || (api-extractor run)",
61
+ "build": "npm-run-all -p build-check build-ts && npm run api-extractor",
62
+ "build-check": "tsgo",
63
+ "build-ts": "tsgo -p tsconfig.build.json",
64
+ "clean": "../../common/scripts/clean-command-state.sh && rm -rf esm api temp",
65
+ "format-check": "oxfmt --check .",
66
+ "format-write": "oxfmt .",
67
+ "lint": "oxlint . --type-aware --quiet && eslint .",
68
+ "lint-fix": "oxlint . --type-aware --quiet --fix && eslint . --fix",
69
+ "validate": "npm run lint && npm run format-check"
70
+ }
71
+ }