@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 +7 -0
- package/esm/auth.d.ts +13 -0
- package/esm/auth.d.ts.map +1 -0
- package/esm/auth.js +20 -0
- package/esm/auth.js.map +1 -0
- package/esm/constants.d.ts +25 -0
- package/esm/constants.d.ts.map +1 -0
- package/esm/constants.js +26 -0
- package/esm/constants.js.map +1 -0
- package/esm/goodmock.d.ts +85 -0
- package/esm/goodmock.d.ts.map +1 -0
- package/esm/goodmock.js +227 -0
- package/esm/goodmock.js.map +1 -0
- package/esm/helpers/mouse-actions.d.ts +21 -0
- package/esm/helpers/mouse-actions.d.ts.map +1 -0
- package/esm/helpers/mouse-actions.js +35 -0
- package/esm/helpers/mouse-actions.js.map +1 -0
- package/esm/index.d.ts +6 -0
- package/esm/index.d.ts.map +1 -0
- package/esm/index.js +8 -0
- package/esm/index.js.map +1 -0
- package/esm/playwright.d.ts +91 -0
- package/esm/playwright.d.ts.map +1 -0
- package/esm/playwright.js +182 -0
- package/esm/playwright.js.map +1 -0
- package/esm/sdk-e2e-utils.d.ts +256 -0
- package/esm/tsdoc-metadata.json +11 -0
- package/package.json +71 -0
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
|
package/esm/auth.js.map
ADDED
|
@@ -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"}
|
package/esm/constants.js
ADDED
|
@@ -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"}
|
package/esm/goodmock.js
ADDED
|
@@ -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":"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"}
|
|
@@ -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
|
package/esm/index.js.map
ADDED
|
@@ -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
|
+
}
|