@gooddata/sdk-e2e-utils 11.46.0-alpha.3 → 11.46.0-alpha.5
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/esm/goodmock.d.ts +68 -15
- package/esm/goodmock.d.ts.map +1 -1
- package/esm/goodmock.js +103 -44
- package/esm/index.d.ts +1 -1
- package/esm/index.d.ts.map +1 -1
- package/esm/playwright.d.ts +7 -1
- package/esm/playwright.d.ts.map +1 -1
- package/esm/playwright.js +8 -1
- package/esm/sdk-e2e-utils.d.ts +88 -15
- package/package.json +3 -3
package/esm/goodmock.d.ts
CHANGED
|
@@ -44,14 +44,19 @@ export declare function goodmockMode(): GoodmockMode;
|
|
|
44
44
|
* {@link resetMappings} in recording mode.
|
|
45
45
|
*/
|
|
46
46
|
export declare function startRecording(host: string, backendHost: string): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
47
50
|
export interface IGoodmockMapping {
|
|
48
51
|
request: {
|
|
49
52
|
url?: string;
|
|
53
|
+
urlPath?: string;
|
|
50
54
|
bodyPatterns?: unknown;
|
|
51
55
|
};
|
|
52
56
|
response: {
|
|
53
57
|
headers?: Record<string, string>;
|
|
54
|
-
jsonBody?:
|
|
58
|
+
jsonBody?: unknown;
|
|
59
|
+
body?: string;
|
|
55
60
|
};
|
|
56
61
|
}
|
|
57
62
|
/**
|
|
@@ -61,25 +66,73 @@ export interface IWorkspaceIdMapping {
|
|
|
61
66
|
sourceWorkspaceId: string;
|
|
62
67
|
targetWorkspaceId: string;
|
|
63
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* @internal
|
|
71
|
+
* Rewrite of a secret value in saved recordings. Every occurrence of `secret` (request bodies,
|
|
72
|
+
* response bodies, URLs) is replaced with `placeholder`, so tests can type the placeholder at
|
|
73
|
+
* replay time and still match the recorded request exactly.
|
|
74
|
+
*/
|
|
75
|
+
export interface ISecretMapping {
|
|
76
|
+
secret: string;
|
|
77
|
+
placeholder: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* @internal
|
|
81
|
+
* A credential shape that must never appear in a saved recording (checked against the fully
|
|
82
|
+
* serialized output). Provide domain-specific shapes (e.g. a provider's API key format) via
|
|
83
|
+
* {@link ISnapshotAndSaveRecordingOptions.leakPatterns} — this module has no built-in patterns of
|
|
84
|
+
* its own, since it has no knowledge of what a given consumer's traffic considers a secret.
|
|
85
|
+
*/
|
|
86
|
+
export interface ILeakPattern {
|
|
87
|
+
label: string;
|
|
88
|
+
pattern: RegExp;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* @internal
|
|
92
|
+
*/
|
|
93
|
+
export interface ISnapshotAndSaveRecordingOptions {
|
|
94
|
+
/** Source/target workspace ID rewrite(s) applied before mappings are saved. */
|
|
95
|
+
workspaceIdMappings?: IWorkspaceIdMapping | IWorkspaceIdMapping[];
|
|
96
|
+
/**
|
|
97
|
+
* Real backend URL (e.g. "https://example.gooddata.com"). When provided together with
|
|
98
|
+
* baseUrl, all occurrences are replaced in the saved mappings so that recorded responses
|
|
99
|
+
* (e.g. geo tile URLs) resolve correctly during replay.
|
|
100
|
+
*/
|
|
101
|
+
backendHost?: string;
|
|
102
|
+
/** App base URL (e.g. "http://kpi-dashboards-ui:9500") that replaces backendHost references. */
|
|
103
|
+
baseUrl?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Secret rewrites. Each secret value is replaced by its placeholder everywhere in the saved
|
|
106
|
+
* mappings (request bodyPatterns included, and masked variants such as "sk-prox***abcd").
|
|
107
|
+
* Saving fails hard if any secret still remains afterwards.
|
|
108
|
+
*/
|
|
109
|
+
secretMappings?: ISecretMapping[];
|
|
110
|
+
/**
|
|
111
|
+
* Credential-shaped patterns that must never appear anywhere in the saved output, regardless
|
|
112
|
+
* of which field they came from. A backstop for secrets `secretMappings` doesn't know about
|
|
113
|
+
* (e.g. a value a backend echoed back that the test never typed). Saving fails hard on a match.
|
|
114
|
+
*/
|
|
115
|
+
leakPatterns?: ILeakPattern[];
|
|
116
|
+
/**
|
|
117
|
+
* Consumer-supplied hook to redact domain-specific secrets from the collected mappings (e.g.
|
|
118
|
+
* blank a `content.apiKey` field on a settings entity) before they're merged and written out.
|
|
119
|
+
* Runs once, right after the built-in credential handling (login cookie, resolveSettings map
|
|
120
|
+
* tokens) and before workspace-id rewriting.
|
|
121
|
+
*/
|
|
122
|
+
sanitizeMappings?: (mappings: IGoodmockMapping[]) => IGoodmockMapping[];
|
|
123
|
+
}
|
|
64
124
|
/**
|
|
65
125
|
* @internal
|
|
66
126
|
* Snapshot goodmock recordings, save the combined mappings to a JSON file
|
|
67
127
|
* on disk, and sanitize any credentials from the output.
|
|
68
128
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
129
|
+
* A single snapshot is taken with repeatsAsScenarios enabled: goodmock turns a
|
|
130
|
+
* request into a scenario chain only when its responses differ across the
|
|
131
|
+
* recording, so stateful sequences are preserved and everything else stays flat.
|
|
72
132
|
*
|
|
73
|
-
* @param host -
|
|
74
|
-
* @param mappingFilePath -
|
|
75
|
-
* @param
|
|
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.
|
|
133
|
+
* @param host - Goodmock host:port (e.g. "backend-mock:8080")
|
|
134
|
+
* @param mappingFilePath - Absolute path to write the mapping JSON file to
|
|
135
|
+
* @param options - See {@link ISnapshotAndSaveRecordingOptions}.
|
|
83
136
|
*/
|
|
84
|
-
export declare function snapshotAndSaveRecording(host: string, mappingFilePath: string,
|
|
137
|
+
export declare function snapshotAndSaveRecording(host: string, mappingFilePath: string, options?: ISnapshotAndSaveRecordingOptions): Promise<void>;
|
|
85
138
|
//# sourceMappingURL=goodmock.d.ts.map
|
package/esm/goodmock.d.ts.map
CHANGED
|
@@ -1 +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;
|
|
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;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACpE,QAAQ,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACrF;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAC7C,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,mBAAmB,GAAG,mBAAmB,EAAE,CAAC;IAClE;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gGAAgG;IAChG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,cAAc,EAAE,CAAC;IAClC;;;;OAIG;IACH,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC;IAC9B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,CAAC;CAC3E;AAqBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAC1C,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,MAAM,EACvB,OAAO,GAAE,gCAAqC,GAC/C,OAAO,CAAC,IAAI,CAAC,CAkKf"}
|
package/esm/goodmock.js
CHANGED
|
@@ -139,47 +139,39 @@ function sanitizeWorkspaceId(mappings, sourceWorkspaceId, targetWorkspaceId) {
|
|
|
139
139
|
* Snapshot goodmock recordings, save the combined mappings to a JSON file
|
|
140
140
|
* on disk, and sanitize any credentials from the output.
|
|
141
141
|
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
142
|
+
* A single snapshot is taken with repeatsAsScenarios enabled: goodmock turns a
|
|
143
|
+
* request into a scenario chain only when its responses differ across the
|
|
144
|
+
* recording, so stateful sequences are preserved and everything else stays flat.
|
|
145
145
|
*
|
|
146
|
-
* @param host -
|
|
147
|
-
* @param mappingFilePath -
|
|
148
|
-
* @param
|
|
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.
|
|
146
|
+
* @param host - Goodmock host:port (e.g. "backend-mock:8080")
|
|
147
|
+
* @param mappingFilePath - Absolute path to write the mapping JSON file to
|
|
148
|
+
* @param options - See {@link ISnapshotAndSaveRecordingOptions}.
|
|
156
149
|
*/
|
|
157
|
-
export async function snapshotAndSaveRecording(host, mappingFilePath,
|
|
158
|
-
|
|
159
|
-
|
|
150
|
+
export async function snapshotAndSaveRecording(host, mappingFilePath, options = {}) {
|
|
151
|
+
const { workspaceIdMappings, backendHost, baseUrl, secretMappings, leakPatterns, sanitizeMappings } = options;
|
|
152
|
+
// Snapshot everything with scenarios enabled. goodmock only emits a scenario
|
|
153
|
+
// chain when a given request produced *different* responses across the
|
|
154
|
+
// recording; identical repeats collapse to a single mapping. This captures
|
|
155
|
+
// stateful sequences (e.g. a GET whose result changes after a POST) that the
|
|
156
|
+
// old per-URL dedup path silently dropped, while keeping idempotent traffic flat.
|
|
157
|
+
const snapshotRes = await fetch(`http://${host}/__admin/recordings/snapshot`, {
|
|
160
158
|
method: "POST",
|
|
161
159
|
headers: { "Content-Type": "application/json" },
|
|
162
160
|
body: JSON.stringify({
|
|
163
161
|
repeatsAsScenarios: true,
|
|
164
|
-
filters: { urlPattern: ".*executionResults.*" },
|
|
165
162
|
persist: false,
|
|
166
163
|
...snapshotParams,
|
|
167
164
|
}),
|
|
168
165
|
});
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
...snapshotParams,
|
|
179
|
-
}),
|
|
180
|
-
});
|
|
181
|
-
const plainData = (await plainRes.json());
|
|
182
|
-
const mappings = [...plainData.mappings, ...scenariosData.mappings];
|
|
166
|
+
const snapshotData = (await snapshotRes.json());
|
|
167
|
+
const mappings = [...snapshotData.mappings];
|
|
168
|
+
// An empty snapshot means the recording journal was lost (e.g. a worker crash re-ran the
|
|
169
|
+
// beforeAll reset mid-spec) or the spec made no requests at all. Never overwrite a previous
|
|
170
|
+
// recording with an empty one, and fail the run so the broken recording cannot go unnoticed.
|
|
171
|
+
if (mappings.length === 0) {
|
|
172
|
+
throw new Error(`Recording snapshot for ${mappingFilePath} is empty — keeping the existing mapping ` +
|
|
173
|
+
`file. Investigate (worker crash mid-spec? goodmock proxy misconfigured?) and re-record.`);
|
|
174
|
+
}
|
|
183
175
|
// Sanitize credentials
|
|
184
176
|
for (const mapping of mappings) {
|
|
185
177
|
if (mapping?.request?.url === "/gdc/account/login") {
|
|
@@ -188,37 +180,104 @@ export async function snapshotAndSaveRecording(host, mappingFilePath, workspaceI
|
|
|
188
180
|
delete mapping.response.headers["Set-Cookie"];
|
|
189
181
|
}
|
|
190
182
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
183
|
+
// Sanitize secrets from resolveSettings responses. Match both org-level
|
|
184
|
+
// (/api/v1/actions/resolveSettings) and workspace-level variants, via either `url`
|
|
185
|
+
// (no query) or `urlPath` (with query params).
|
|
186
|
+
const requestPath = mapping?.request?.url ?? mapping?.request?.urlPath;
|
|
187
|
+
if (typeof requestPath === "string" && requestPath.includes("/resolveSettings")) {
|
|
194
188
|
try {
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
189
|
+
const body = mapping?.response?.jsonBody;
|
|
190
|
+
const settings = Array.isArray(body)
|
|
191
|
+
? body
|
|
192
|
+
: Array.isArray(body?.data)
|
|
193
|
+
? body.data
|
|
194
|
+
: null;
|
|
195
|
+
if (settings) {
|
|
196
|
+
for (const setting of settings) {
|
|
197
|
+
const content = setting?.content;
|
|
198
|
+
if (!content || typeof content !== "object") {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
// Map tokens (agGrid, mapbox) are stored under content.value.
|
|
202
|
+
if (setting?.id === "agGridToken" || setting?.id === "mapboxToken") {
|
|
203
|
+
content["value"] = "";
|
|
202
204
|
}
|
|
203
205
|
}
|
|
204
|
-
mapping.response.jsonBody = parsedBody;
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
catch {
|
|
208
|
-
console.warn(`sanitizeCredentials – resolveSettings body is not valid JSON for
|
|
209
|
+
console.warn(`sanitizeCredentials – resolveSettings body is not valid JSON for: ${requestPath}`);
|
|
209
210
|
}
|
|
210
211
|
}
|
|
211
212
|
}
|
|
213
|
+
// Consumer-supplied redaction for secrets this module has no knowledge of (e.g. an LLM
|
|
214
|
+
// provider's API key echoed back on a settings entity).
|
|
215
|
+
const sanitizedByCaller = sanitizeMappings ? sanitizeMappings(mappings) : mappings;
|
|
212
216
|
const mappingsToApply = Array.isArray(workspaceIdMappings)
|
|
213
217
|
? workspaceIdMappings
|
|
214
218
|
: workspaceIdMappings
|
|
215
219
|
? [workspaceIdMappings]
|
|
216
220
|
: [];
|
|
217
|
-
const sanitizedMappings = mappingsToApply.reduce((acc, { sourceWorkspaceId, targetWorkspaceId }) => sanitizeWorkspaceId(acc, sourceWorkspaceId, targetWorkspaceId),
|
|
221
|
+
const sanitizedMappings = mappingsToApply.reduce((acc, { sourceWorkspaceId, targetWorkspaceId }) => sanitizeWorkspaceId(acc, sourceWorkspaceId, targetWorkspaceId), sanitizedByCaller);
|
|
218
222
|
let output = JSON.stringify({ mappings: sanitizedMappings }, null, 4) + "\n";
|
|
219
223
|
if (backendHost && baseUrl) {
|
|
220
224
|
output = output.replaceAll(backendHost, baseUrl);
|
|
221
225
|
}
|
|
226
|
+
// Replace secrets typed into the UI / sent by API helpers during recording with their
|
|
227
|
+
// replay-time placeholders. JSON-escape both sides so values containing characters that
|
|
228
|
+
// JSON.stringify escapes (quotes, backslashes) are still found in the serialized output.
|
|
229
|
+
// Provider error messages echo keys in a masked form ("sk-prox***********abcd"), so that
|
|
230
|
+
// shape is rewritten too — it still leaks the ends of a real key.
|
|
231
|
+
const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
232
|
+
// Below this, prefix/suffix would overlap or leave nothing to mask — there's no safe
|
|
233
|
+
// "partial reveal" shape to detect, so such secrets rely on the exact-match replace above.
|
|
234
|
+
const MIN_MASKABLE_SECRET_LENGTH = 6;
|
|
235
|
+
// Masking keeps a variable-length prefix/suffix of the key around the asterisks; scale how
|
|
236
|
+
// much we match by the secret's own length instead of assuming a fixed provider convention
|
|
237
|
+
// (e.g. OpenAI's "sk-prox***********abcd") so shorter secrets and other providers' masking
|
|
238
|
+
// shapes are still caught.
|
|
239
|
+
const buildMaskedFormPattern = (escapedSecret) => {
|
|
240
|
+
if (escapedSecret.length < MIN_MASKABLE_SECRET_LENGTH) {
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|
|
243
|
+
const prefixLength = Math.min(7, Math.floor(escapedSecret.length / 3));
|
|
244
|
+
const suffixLength = Math.min(4, Math.floor(escapedSecret.length / 4));
|
|
245
|
+
return new RegExp(`${escapeRegex(escapedSecret.slice(0, prefixLength))}[^"*\\\\]*[*]+[^"*\\\\]*${escapeRegex(escapedSecret.slice(-suffixLength))}`, "g");
|
|
246
|
+
};
|
|
247
|
+
const effectiveSecretMappings = (secretMappings ?? []).filter(({ secret, placeholder }) => secret && secret !== placeholder);
|
|
248
|
+
for (const { secret, placeholder } of effectiveSecretMappings) {
|
|
249
|
+
const escapedSecret = JSON.stringify(secret).slice(1, -1);
|
|
250
|
+
const escapedPlaceholder = JSON.stringify(placeholder).slice(1, -1);
|
|
251
|
+
output = output.replaceAll(escapedSecret, escapedPlaceholder);
|
|
252
|
+
const maskedForm = buildMaskedFormPattern(escapedSecret);
|
|
253
|
+
if (maskedForm) {
|
|
254
|
+
output = output.replace(maskedForm, escapedPlaceholder);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// Leak guard: never write a recording that still contains a real secret (full or masked).
|
|
258
|
+
for (const { secret } of effectiveSecretMappings) {
|
|
259
|
+
const escapedSecret = JSON.stringify(secret).slice(1, -1);
|
|
260
|
+
const maskedForm = buildMaskedFormPattern(escapedSecret);
|
|
261
|
+
if (output.includes(escapedSecret) || output.includes(secret) || maskedForm?.test(output)) {
|
|
262
|
+
throw new Error(`Recording for ${mappingFilePath} still contains a secret after sanitization ` +
|
|
263
|
+
`(e.g. in an encoded form). Refusing to save the mapping file.`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Pattern guard: catch credential-shaped values from ANY source (not just the ones the
|
|
267
|
+
// tests typed), e.g. a real key stored in a backend org setting under an unexpected field.
|
|
268
|
+
// Patterns are consumer-supplied — this module has no built-in notion of what a secret
|
|
269
|
+
// looks like for a given domain (see ISnapshotAndSaveRecordingOptions.leakPatterns).
|
|
270
|
+
for (const { label, pattern } of leakPatterns ?? []) {
|
|
271
|
+
// Reset lastIndex in case pattern carries the g/y flag — exec() on a stateful regex
|
|
272
|
+
// would otherwise resume from wherever a previous call (e.g. a prior recording) left off.
|
|
273
|
+
pattern.lastIndex = 0;
|
|
274
|
+
const match = pattern.exec(output);
|
|
275
|
+
if (match) {
|
|
276
|
+
throw new Error(`Recording for ${mappingFilePath} contains a value shaped like a ${label} ` +
|
|
277
|
+
`("${match[0].slice(0, 8)}…"). Refusing to save the mapping file — extend ` +
|
|
278
|
+
`sanitizeMappings / secretMappings so it gets sanitized.`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
222
281
|
mkdirSync(dirname(mappingFilePath), { recursive: true });
|
|
223
282
|
writeFileSync(mappingFilePath, output);
|
|
224
283
|
// eslint-disable-next-line no-console
|
package/esm/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { authHeader, authedRouteFetch, injectAuthHeader } from "./auth.js";
|
|
2
|
-
export { type IWorkspaceIdMapping, loadMappings, resetMappings, resetScenarios, mockLogRequests, goodmockMode, GoodmockMode, startRecording, snapshotAndSaveRecording, } from "./goodmock.js";
|
|
2
|
+
export { type IGoodmockMapping, type ILeakPattern, type ISecretMapping, type ISnapshotAndSaveRecordingOptions, type IWorkspaceIdMapping, loadMappings, resetMappings, resetScenarios, mockLogRequests, goodmockMode, GoodmockMode, startRecording, snapshotAndSaveRecording, } from "./goodmock.js";
|
|
3
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
4
|
export { clickByBoundingBox, hoverByBoundingBox } from "./helpers/mouse-actions.js";
|
|
5
5
|
export { GOODMOCK_HOST, API_TOKEN, BACKEND_HOST, getBaseUrl, getEnvWithFallback, getWorkspaceId, getDangerWorkspaceId, } from "./constants.js";
|
package/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC3E,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,EACd,oBAAoB,GACvB,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC3E,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,gCAAgC,EACrC,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,EACd,oBAAoB,GACvB,MAAM,gBAAgB,CAAC"}
|
package/esm/playwright.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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";
|
|
2
|
+
import { type IGoodmockMapping, type ILeakPattern, type ISecretMapping, type IWorkspaceIdMapping } from "./goodmock.js";
|
|
3
3
|
/**
|
|
4
4
|
* @internal
|
|
5
5
|
*/
|
|
@@ -9,6 +9,12 @@ export interface IGoodmockOptions {
|
|
|
9
9
|
getMappingPath: (specFile: string) => string;
|
|
10
10
|
workspaceIdMappings?: IWorkspaceIdMapping | IWorkspaceIdMapping[];
|
|
11
11
|
baseUrl?: string;
|
|
12
|
+
/** Secret values replaced by placeholders in saved recordings (see {@link ISecretMapping}). */
|
|
13
|
+
secretMappings?: ISecretMapping[];
|
|
14
|
+
/** Credential-shaped patterns that must never appear in saved recordings (see {@link ILeakPattern}). */
|
|
15
|
+
leakPatterns?: ILeakPattern[];
|
|
16
|
+
/** Consumer-supplied redaction hook for domain-specific secrets (see snapshotAndSaveRecording). */
|
|
17
|
+
sanitizeMappings?: (mappings: IGoodmockMapping[]) => IGoodmockMapping[];
|
|
12
18
|
}
|
|
13
19
|
/**
|
|
14
20
|
* @internal
|
package/esm/playwright.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../src/playwright.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EAChB,IAAI,EACP,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAEH,KAAK,mBAAmB,EAM3B,MAAM,eAAe,CAAC;AAIvB;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C,mBAAmB,CAAC,EAAE,mBAAmB,GAAG,mBAAmB,EAAE,CAAC;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../src/playwright.ts"],"names":[],"mappings":"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"}
|
package/esm/playwright.js
CHANGED
|
@@ -106,7 +106,14 @@ export function createTest(options = {}) {
|
|
|
106
106
|
});
|
|
107
107
|
testInstance.afterAll(async () => {
|
|
108
108
|
if (goodmockMode === GoodmockMode.Record) {
|
|
109
|
-
await snapshotAndSaveRecording(gm.host, gm.getMappingPath(specName),
|
|
109
|
+
await snapshotAndSaveRecording(gm.host, gm.getMappingPath(specName), {
|
|
110
|
+
workspaceIdMappings: gm.workspaceIdMappings,
|
|
111
|
+
backendHost: gm.backendHost,
|
|
112
|
+
baseUrl: gm.baseUrl,
|
|
113
|
+
secretMappings: gm.secretMappings,
|
|
114
|
+
leakPatterns: gm.leakPatterns,
|
|
115
|
+
sanitizeMappings: gm.sanitizeMappings,
|
|
116
|
+
});
|
|
110
117
|
}
|
|
111
118
|
await resetMappings(gm.host);
|
|
112
119
|
});
|
package/esm/sdk-e2e-utils.d.ts
CHANGED
|
@@ -183,6 +183,22 @@ export declare interface IFeatureHubFeature {
|
|
|
183
183
|
v?: string;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
/**
|
|
187
|
+
* @internal
|
|
188
|
+
*/
|
|
189
|
+
export declare interface IGoodmockMapping {
|
|
190
|
+
request: {
|
|
191
|
+
url?: string;
|
|
192
|
+
urlPath?: string;
|
|
193
|
+
bodyPatterns?: unknown;
|
|
194
|
+
};
|
|
195
|
+
response: {
|
|
196
|
+
headers?: Record<string, string>;
|
|
197
|
+
jsonBody?: unknown;
|
|
198
|
+
body?: string;
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
186
202
|
/**
|
|
187
203
|
* @internal
|
|
188
204
|
*/
|
|
@@ -192,6 +208,24 @@ export declare interface IGoodmockOptions {
|
|
|
192
208
|
getMappingPath: (specFile: string) => string;
|
|
193
209
|
workspaceIdMappings?: IWorkspaceIdMapping | IWorkspaceIdMapping[];
|
|
194
210
|
baseUrl?: string;
|
|
211
|
+
/** Secret values replaced by placeholders in saved recordings (see {@link ISecretMapping}). */
|
|
212
|
+
secretMappings?: ISecretMapping[];
|
|
213
|
+
/** Credential-shaped patterns that must never appear in saved recordings (see {@link ILeakPattern}). */
|
|
214
|
+
leakPatterns?: ILeakPattern[];
|
|
215
|
+
/** Consumer-supplied redaction hook for domain-specific secrets (see snapshotAndSaveRecording). */
|
|
216
|
+
sanitizeMappings?: (mappings: IGoodmockMapping[]) => IGoodmockMapping[];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @internal
|
|
221
|
+
* A credential shape that must never appear in a saved recording (checked against the fully
|
|
222
|
+
* serialized output). Provide domain-specific shapes (e.g. a provider's API key format) via
|
|
223
|
+
* {@link ISnapshotAndSaveRecordingOptions.leakPatterns} — this module has no built-in patterns of
|
|
224
|
+
* its own, since it has no knowledge of what a given consumer's traffic considers a secret.
|
|
225
|
+
*/
|
|
226
|
+
export declare interface ILeakPattern {
|
|
227
|
+
label: string;
|
|
228
|
+
pattern: RegExp;
|
|
195
229
|
}
|
|
196
230
|
|
|
197
231
|
/**
|
|
@@ -200,6 +234,52 @@ export declare interface IGoodmockOptions {
|
|
|
200
234
|
*/
|
|
201
235
|
export declare function injectAuthHeader(page: Page, token: string): Promise<void>;
|
|
202
236
|
|
|
237
|
+
/**
|
|
238
|
+
* @internal
|
|
239
|
+
* Rewrite of a secret value in saved recordings. Every occurrence of `secret` (request bodies,
|
|
240
|
+
* response bodies, URLs) is replaced with `placeholder`, so tests can type the placeholder at
|
|
241
|
+
* replay time and still match the recorded request exactly.
|
|
242
|
+
*/
|
|
243
|
+
export declare interface ISecretMapping {
|
|
244
|
+
secret: string;
|
|
245
|
+
placeholder: string;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @internal
|
|
250
|
+
*/
|
|
251
|
+
export declare interface ISnapshotAndSaveRecordingOptions {
|
|
252
|
+
/** Source/target workspace ID rewrite(s) applied before mappings are saved. */
|
|
253
|
+
workspaceIdMappings?: IWorkspaceIdMapping | IWorkspaceIdMapping[];
|
|
254
|
+
/**
|
|
255
|
+
* Real backend URL (e.g. "https://example.gooddata.com"). When provided together with
|
|
256
|
+
* baseUrl, all occurrences are replaced in the saved mappings so that recorded responses
|
|
257
|
+
* (e.g. geo tile URLs) resolve correctly during replay.
|
|
258
|
+
*/
|
|
259
|
+
backendHost?: string;
|
|
260
|
+
/** App base URL (e.g. "http://kpi-dashboards-ui:9500") that replaces backendHost references. */
|
|
261
|
+
baseUrl?: string;
|
|
262
|
+
/**
|
|
263
|
+
* Secret rewrites. Each secret value is replaced by its placeholder everywhere in the saved
|
|
264
|
+
* mappings (request bodyPatterns included, and masked variants such as "sk-prox***abcd").
|
|
265
|
+
* Saving fails hard if any secret still remains afterwards.
|
|
266
|
+
*/
|
|
267
|
+
secretMappings?: ISecretMapping[];
|
|
268
|
+
/**
|
|
269
|
+
* Credential-shaped patterns that must never appear anywhere in the saved output, regardless
|
|
270
|
+
* of which field they came from. A backstop for secrets `secretMappings` doesn't know about
|
|
271
|
+
* (e.g. a value a backend echoed back that the test never typed). Saving fails hard on a match.
|
|
272
|
+
*/
|
|
273
|
+
leakPatterns?: ILeakPattern[];
|
|
274
|
+
/**
|
|
275
|
+
* Consumer-supplied hook to redact domain-specific secrets from the collected mappings (e.g.
|
|
276
|
+
* blank a `content.apiKey` field on a settings entity) before they're merged and written out.
|
|
277
|
+
* Runs once, right after the built-in credential handling (login cookie, resolveSettings map
|
|
278
|
+
* tokens) and before workspace-id rewriting.
|
|
279
|
+
*/
|
|
280
|
+
sanitizeMappings?: (mappings: IGoodmockMapping[]) => IGoodmockMapping[];
|
|
281
|
+
}
|
|
282
|
+
|
|
203
283
|
/**
|
|
204
284
|
* @internal
|
|
205
285
|
*/
|
|
@@ -243,22 +323,15 @@ export declare function resetScenarios(host: string): Promise<void>;
|
|
|
243
323
|
* Snapshot goodmock recordings, save the combined mappings to a JSON file
|
|
244
324
|
* on disk, and sanitize any credentials from the output.
|
|
245
325
|
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
326
|
+
* A single snapshot is taken with repeatsAsScenarios enabled: goodmock turns a
|
|
327
|
+
* request into a scenario chain only when its responses differ across the
|
|
328
|
+
* recording, so stateful sequences are preserved and everything else stays flat.
|
|
249
329
|
*
|
|
250
|
-
* @param host -
|
|
251
|
-
* @param mappingFilePath -
|
|
252
|
-
* @param
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
* When provided together with baseUrl, all occurrences are replaced in the
|
|
256
|
-
* saved mappings so that recorded responses (e.g. geo tile URLs) resolve
|
|
257
|
-
* correctly during replay.
|
|
258
|
-
* @param baseUrl - Optional app base URL (e.g. "http://kpi-dashboards-ui:9500")
|
|
259
|
-
* that replaces backendHost references in the saved mappings.
|
|
260
|
-
*/
|
|
261
|
-
export declare function snapshotAndSaveRecording(host: string, mappingFilePath: string, workspaceIdMappings?: IWorkspaceIdMapping | IWorkspaceIdMapping[], backendHost?: string, baseUrl?: string): Promise<void>;
|
|
330
|
+
* @param host - Goodmock host:port (e.g. "backend-mock:8080")
|
|
331
|
+
* @param mappingFilePath - Absolute path to write the mapping JSON file to
|
|
332
|
+
* @param options - See {@link ISnapshotAndSaveRecordingOptions}.
|
|
333
|
+
*/
|
|
334
|
+
export declare function snapshotAndSaveRecording(host: string, mappingFilePath: string, options?: ISnapshotAndSaveRecordingOptions): Promise<void>;
|
|
262
335
|
|
|
263
336
|
/**
|
|
264
337
|
* @internal
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gooddata/sdk-e2e-utils",
|
|
3
|
-
"version": "11.46.0-alpha.
|
|
3
|
+
"version": "11.46.0-alpha.5",
|
|
4
4
|
"description": "GoodData utility functions for Playwright E2E tests",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "GoodData",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"oxlint": "1.51.0",
|
|
52
52
|
"oxlint-tsgolint": "0.15.0",
|
|
53
53
|
"typescript": "5.9.3",
|
|
54
|
-
"@gooddata/eslint-config": "11.46.0-alpha.
|
|
55
|
-
"@gooddata/oxlint-config": "11.46.0-alpha.
|
|
54
|
+
"@gooddata/eslint-config": "11.46.0-alpha.5",
|
|
55
|
+
"@gooddata/oxlint-config": "11.46.0-alpha.5"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"_phase:build": "npm run build",
|