@riddledc/riddle-proof 0.7.26 → 0.7.27
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/dist/{chunk-SDSBS64X.js → chunk-FUWINYNX.js} +172 -1
- package/dist/cli.cjs +172 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +172 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/profile.cjs +172 -1
- package/dist/profile.d.cts +14 -1
- package/dist/profile.d.ts +14 -1
- package/dist/profile.js +1 -1
- package/package.json +1 -1
|
@@ -119,6 +119,11 @@ function jsonRecord(value) {
|
|
|
119
119
|
if (!isRecord(value)) return void 0;
|
|
120
120
|
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
|
|
121
121
|
}
|
|
122
|
+
function stringRecord(value) {
|
|
123
|
+
if (!isRecord(value)) return void 0;
|
|
124
|
+
const entries = Object.entries(value).map(([key, child]) => [key, String(child)]).filter(([key]) => key.trim());
|
|
125
|
+
return entries.length ? Object.fromEntries(entries) : void 0;
|
|
126
|
+
}
|
|
122
127
|
function toJsonValue(value) {
|
|
123
128
|
if (value === null || value === void 0) return null;
|
|
124
129
|
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
@@ -190,6 +195,33 @@ function normalizeSetupActions(value) {
|
|
|
190
195
|
if (!Array.isArray(value)) throw new Error("target.setup_actions must be an array.");
|
|
191
196
|
return value.map(normalizeSetupAction);
|
|
192
197
|
}
|
|
198
|
+
function normalizeNetworkMock(input, index) {
|
|
199
|
+
if (!isRecord(input)) throw new Error(`target.network_mocks[${index}] must be an object.`);
|
|
200
|
+
const url = stringValue(input.url) || stringValue(input.glob) || stringValue(input.pattern);
|
|
201
|
+
if (!url) throw new Error(`target.network_mocks[${index}] requires url.`);
|
|
202
|
+
const status = numberValue(input.status) ?? 200;
|
|
203
|
+
if (!Number.isInteger(status) || status < 100 || status > 599) {
|
|
204
|
+
throw new Error(`target.network_mocks[${index}].status must be an HTTP status code.`);
|
|
205
|
+
}
|
|
206
|
+
const body = stringValue(input.body) ?? stringValue(input.body_text) ?? stringValue(input.bodyText);
|
|
207
|
+
const hasJsonBody = Object.prototype.hasOwnProperty.call(input, "body_json") || Object.prototype.hasOwnProperty.call(input, "bodyJson") || Object.prototype.hasOwnProperty.call(input, "json");
|
|
208
|
+
return {
|
|
209
|
+
label: normalizeName(input.label || input.name, `network-mock-${index + 1}`),
|
|
210
|
+
url,
|
|
211
|
+
method: stringValue(input.method)?.toUpperCase(),
|
|
212
|
+
status,
|
|
213
|
+
content_type: stringValue(input.content_type) || stringValue(input.contentType),
|
|
214
|
+
headers: stringRecord(input.headers),
|
|
215
|
+
body,
|
|
216
|
+
body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : void 0,
|
|
217
|
+
required: input.required === false ? false : true
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function normalizeNetworkMocks(value) {
|
|
221
|
+
if (value === void 0) return void 0;
|
|
222
|
+
if (!Array.isArray(value)) throw new Error("target.network_mocks must be an array.");
|
|
223
|
+
return value.map(normalizeNetworkMock);
|
|
224
|
+
}
|
|
193
225
|
function normalizeCheck(input, index) {
|
|
194
226
|
if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
195
227
|
const type = stringValue(input.type);
|
|
@@ -259,7 +291,8 @@ function normalizeRiddleProofProfile(input, options = {}) {
|
|
|
259
291
|
timeout_sec: timeoutSecValue(targetInput.timeout_sec) ?? timeoutSecValue(targetInput.timeoutSec) ?? timeoutSecValue(targetInput.riddle_timeout_sec) ?? timeoutSecValue(targetInput.riddleTimeoutSec),
|
|
260
292
|
wait_for_selector: stringValue(targetInput.wait_for_selector) || stringValue(targetInput.waitForSelector),
|
|
261
293
|
wait_ms: numberValue(targetInput.wait_ms) ?? numberValue(targetInput.waitMs),
|
|
262
|
-
setup_actions: normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions)
|
|
294
|
+
setup_actions: normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions),
|
|
295
|
+
network_mocks: normalizeNetworkMocks(targetInput.network_mocks ?? targetInput.networkMocks)
|
|
263
296
|
},
|
|
264
297
|
checks,
|
|
265
298
|
artifacts: Array.isArray(input.artifacts) ? input.artifacts.map((item) => String(item)).filter(Boolean) : ["screenshot", "console", "dom_summary", "proof_json"],
|
|
@@ -531,6 +564,50 @@ function assessSetupActionsFromEvidence(profile, evidence) {
|
|
|
531
564
|
message: failed.length ? `Setup actions failed in ${failed.length} viewport action(s).` : void 0
|
|
532
565
|
};
|
|
533
566
|
}
|
|
567
|
+
function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
568
|
+
const mocks = profile.target.network_mocks || [];
|
|
569
|
+
if (!mocks.length) return void 0;
|
|
570
|
+
const events = evidence.network_mocks || [];
|
|
571
|
+
const failed = [];
|
|
572
|
+
const requiredMocks = mocks.filter((mock) => mock.required !== false);
|
|
573
|
+
for (const mock of requiredMocks) {
|
|
574
|
+
const hits = events.filter((event) => event.label === mock.label && event.ok !== false).length;
|
|
575
|
+
if (hits < 1) {
|
|
576
|
+
failed.push({
|
|
577
|
+
label: mock.label,
|
|
578
|
+
url: mock.url,
|
|
579
|
+
method: mock.method || null,
|
|
580
|
+
reason: "required_mock_not_hit"
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
for (const event of events) {
|
|
585
|
+
if (event.ok === false) {
|
|
586
|
+
failed.push({
|
|
587
|
+
label: event.label ?? null,
|
|
588
|
+
url: event.url ?? null,
|
|
589
|
+
method: event.method ?? null,
|
|
590
|
+
reason: event.reason ?? event.error ?? "mock_failed"
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return {
|
|
595
|
+
type: "network_mocks_succeeded",
|
|
596
|
+
label: "network mocks succeeded",
|
|
597
|
+
status: failed.length ? "failed" : "passed",
|
|
598
|
+
evidence: {
|
|
599
|
+
mock_count: mocks.length,
|
|
600
|
+
required_count: requiredMocks.length,
|
|
601
|
+
hit_count: events.filter((event) => event.ok !== false).length,
|
|
602
|
+
hits_by_label: Object.fromEntries(mocks.map((mock) => [
|
|
603
|
+
mock.label,
|
|
604
|
+
events.filter((event) => event.label === mock.label && event.ok !== false).length
|
|
605
|
+
])),
|
|
606
|
+
failed
|
|
607
|
+
},
|
|
608
|
+
message: failed.length ? `Network mocks failed or were not hit for ${failed.length} mock(s).` : void 0
|
|
609
|
+
};
|
|
610
|
+
}
|
|
534
611
|
function profileStatusFromEvidence(profile, evidence, checks) {
|
|
535
612
|
if (!evidence) return "proof_insufficient";
|
|
536
613
|
const viewports = evidence.viewports || [];
|
|
@@ -545,6 +622,7 @@ function profileStatusFromEvidence(profile, evidence, checks) {
|
|
|
545
622
|
function assessRiddleProofProfileEvidence(profile, evidence, options = {}) {
|
|
546
623
|
const capturedAt = evidence?.captured_at || (/* @__PURE__ */ new Date()).toISOString();
|
|
547
624
|
const checks = evidence ? [
|
|
625
|
+
assessNetworkMocksFromEvidence(profile, evidence),
|
|
548
626
|
assessSetupActionsFromEvidence(profile, evidence),
|
|
549
627
|
...profile.checks.map((check) => assessCheckFromEvidence(check, evidence))
|
|
550
628
|
].filter((check) => Boolean(check)) : [];
|
|
@@ -761,6 +839,49 @@ function horizontalBoundsOverflowPx(value) {
|
|
|
761
839
|
function assessProfile(profile, evidence) {
|
|
762
840
|
const checks = [];
|
|
763
841
|
const viewports = evidence.viewports || [];
|
|
842
|
+
if (profile.target && Array.isArray(profile.target.network_mocks) && profile.target.network_mocks.length) {
|
|
843
|
+
const events = evidence.network_mocks || [];
|
|
844
|
+
const requiredMocks = profile.target.network_mocks.filter((mock) => mock && mock.required !== false);
|
|
845
|
+
const failed = [];
|
|
846
|
+
for (const mock of requiredMocks) {
|
|
847
|
+
const hits = events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
|
|
848
|
+
if (hits < 1) {
|
|
849
|
+
failed.push({
|
|
850
|
+
label: mock.label,
|
|
851
|
+
url: mock.url,
|
|
852
|
+
method: mock.method || null,
|
|
853
|
+
reason: "required_mock_not_hit",
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
for (const event of events) {
|
|
858
|
+
if (event && event.ok === false) {
|
|
859
|
+
failed.push({
|
|
860
|
+
label: event.label || null,
|
|
861
|
+
url: event.url || null,
|
|
862
|
+
method: event.method || null,
|
|
863
|
+
reason: event.reason || event.error || "mock_failed",
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
const hitsByLabel = {};
|
|
868
|
+
for (const mock of profile.target.network_mocks) {
|
|
869
|
+
hitsByLabel[mock.label] = events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
|
|
870
|
+
}
|
|
871
|
+
checks.push({
|
|
872
|
+
type: "network_mocks_succeeded",
|
|
873
|
+
label: "network mocks succeeded",
|
|
874
|
+
status: failed.length ? "failed" : "passed",
|
|
875
|
+
evidence: {
|
|
876
|
+
mock_count: profile.target.network_mocks.length,
|
|
877
|
+
required_count: requiredMocks.length,
|
|
878
|
+
hit_count: events.filter((event) => event && event.ok !== false).length,
|
|
879
|
+
hits_by_label: hitsByLabel,
|
|
880
|
+
failed,
|
|
881
|
+
},
|
|
882
|
+
message: failed.length ? "Network mocks failed or were not hit for " + failed.length + " mock(s)." : undefined,
|
|
883
|
+
});
|
|
884
|
+
}
|
|
764
885
|
if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
|
|
765
886
|
const actionCount = profile.target.setup_actions.length;
|
|
766
887
|
const failed = [];
|
|
@@ -950,6 +1071,7 @@ const profileSlug = ${serializableSlug};
|
|
|
950
1071
|
const capturedAt = new Date().toISOString();
|
|
951
1072
|
const consoleEvents = [];
|
|
952
1073
|
const pageErrors = [];
|
|
1074
|
+
const networkMockEvents = [];
|
|
953
1075
|
page.on("console", (message) => {
|
|
954
1076
|
const type = message.type();
|
|
955
1077
|
if (type === "error" || type === "warning" || type === "assert") {
|
|
@@ -996,6 +1118,49 @@ function compactSetupResultText(value) {
|
|
|
996
1118
|
async function setupLocatorVisible(locator, index) {
|
|
997
1119
|
return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
|
|
998
1120
|
}
|
|
1121
|
+
async function registerNetworkMocks(mocks) {
|
|
1122
|
+
for (const mock of mocks || []) {
|
|
1123
|
+
await page.route(mock.url, async (route) => {
|
|
1124
|
+
const request = route.request();
|
|
1125
|
+
const method = request.method ? request.method() : "";
|
|
1126
|
+
if (mock.method && method.toUpperCase() !== String(mock.method).toUpperCase()) {
|
|
1127
|
+
await route.continue();
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
try {
|
|
1131
|
+
const headers = { ...(mock.headers || {}) };
|
|
1132
|
+
let body = mock.body || "";
|
|
1133
|
+
let contentType = mock.content_type || headers["content-type"] || headers["Content-Type"] || "text/plain";
|
|
1134
|
+
if (mock.body_json !== undefined) {
|
|
1135
|
+
body = JSON.stringify(mock.body_json);
|
|
1136
|
+
contentType = mock.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
|
|
1137
|
+
}
|
|
1138
|
+
networkMockEvents.push({
|
|
1139
|
+
ok: true,
|
|
1140
|
+
label: mock.label,
|
|
1141
|
+
url: request.url(),
|
|
1142
|
+
method,
|
|
1143
|
+
status: mock.status || 200,
|
|
1144
|
+
});
|
|
1145
|
+
await route.fulfill({
|
|
1146
|
+
status: mock.status || 200,
|
|
1147
|
+
headers,
|
|
1148
|
+
contentType,
|
|
1149
|
+
body,
|
|
1150
|
+
});
|
|
1151
|
+
} catch (error) {
|
|
1152
|
+
networkMockEvents.push({
|
|
1153
|
+
ok: false,
|
|
1154
|
+
label: mock.label,
|
|
1155
|
+
url: request.url(),
|
|
1156
|
+
method,
|
|
1157
|
+
error: String(error && error.message ? error.message : error).slice(0, 1000),
|
|
1158
|
+
});
|
|
1159
|
+
throw error;
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
999
1164
|
async function executeSetupAction(action, ordinal) {
|
|
1000
1165
|
const type = setupActionType(action);
|
|
1001
1166
|
const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null };
|
|
@@ -1257,6 +1422,7 @@ function buildProfileEvidence(currentViewports) {
|
|
|
1257
1422
|
fatal_count: consoleEvents.filter((event) => event.type === "error" || event.type === "assert").length,
|
|
1258
1423
|
},
|
|
1259
1424
|
page_errors: pageErrors,
|
|
1425
|
+
network_mocks: networkMockEvents.slice(),
|
|
1260
1426
|
dom_summary: {
|
|
1261
1427
|
expected_viewport_count: expectedViewportCount,
|
|
1262
1428
|
viewport_count: currentViewports.length,
|
|
@@ -1266,6 +1432,8 @@ function buildProfileEvidence(currentViewports) {
|
|
|
1266
1432
|
overflow_px: currentViewports.map((viewport) => viewport.overflow_px),
|
|
1267
1433
|
bounds_overflow_px: currentViewports.map((viewport) => viewport.bounds_overflow_px),
|
|
1268
1434
|
overflow_offender_counts: currentViewports.map((viewport) => (viewport.overflow_offenders || []).length),
|
|
1435
|
+
network_mock_count: (profile.target.network_mocks || []).length,
|
|
1436
|
+
network_mock_hit_count: networkMockEvents.filter((event) => event.ok !== false).length,
|
|
1269
1437
|
},
|
|
1270
1438
|
};
|
|
1271
1439
|
}
|
|
@@ -1279,6 +1447,9 @@ async function saveProfileArtifacts(currentViewports) {
|
|
|
1279
1447
|
}
|
|
1280
1448
|
return result;
|
|
1281
1449
|
}
|
|
1450
|
+
await registerNetworkMocks(profile.target.network_mocks || []);
|
|
1451
|
+
consoleEvents.length = 0;
|
|
1452
|
+
pageErrors.length = 0;
|
|
1282
1453
|
let result = await saveProfileArtifacts(viewports);
|
|
1283
1454
|
for (const viewport of profile.target.viewports || []) {
|
|
1284
1455
|
viewports.push(await captureViewport(viewport));
|
package/dist/cli.cjs
CHANGED
|
@@ -6992,6 +6992,11 @@ function jsonRecord(value) {
|
|
|
6992
6992
|
if (!isRecord(value)) return void 0;
|
|
6993
6993
|
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
|
|
6994
6994
|
}
|
|
6995
|
+
function stringRecord(value) {
|
|
6996
|
+
if (!isRecord(value)) return void 0;
|
|
6997
|
+
const entries = Object.entries(value).map(([key, child]) => [key, String(child)]).filter(([key]) => key.trim());
|
|
6998
|
+
return entries.length ? Object.fromEntries(entries) : void 0;
|
|
6999
|
+
}
|
|
6995
7000
|
function toJsonValue(value) {
|
|
6996
7001
|
if (value === null || value === void 0) return null;
|
|
6997
7002
|
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
@@ -7063,6 +7068,33 @@ function normalizeSetupActions(value) {
|
|
|
7063
7068
|
if (!Array.isArray(value)) throw new Error("target.setup_actions must be an array.");
|
|
7064
7069
|
return value.map(normalizeSetupAction);
|
|
7065
7070
|
}
|
|
7071
|
+
function normalizeNetworkMock(input, index) {
|
|
7072
|
+
if (!isRecord(input)) throw new Error(`target.network_mocks[${index}] must be an object.`);
|
|
7073
|
+
const url = stringValue2(input.url) || stringValue2(input.glob) || stringValue2(input.pattern);
|
|
7074
|
+
if (!url) throw new Error(`target.network_mocks[${index}] requires url.`);
|
|
7075
|
+
const status = numberValue(input.status) ?? 200;
|
|
7076
|
+
if (!Number.isInteger(status) || status < 100 || status > 599) {
|
|
7077
|
+
throw new Error(`target.network_mocks[${index}].status must be an HTTP status code.`);
|
|
7078
|
+
}
|
|
7079
|
+
const body = stringValue2(input.body) ?? stringValue2(input.body_text) ?? stringValue2(input.bodyText);
|
|
7080
|
+
const hasJsonBody = Object.prototype.hasOwnProperty.call(input, "body_json") || Object.prototype.hasOwnProperty.call(input, "bodyJson") || Object.prototype.hasOwnProperty.call(input, "json");
|
|
7081
|
+
return {
|
|
7082
|
+
label: normalizeName(input.label || input.name, `network-mock-${index + 1}`),
|
|
7083
|
+
url,
|
|
7084
|
+
method: stringValue2(input.method)?.toUpperCase(),
|
|
7085
|
+
status,
|
|
7086
|
+
content_type: stringValue2(input.content_type) || stringValue2(input.contentType),
|
|
7087
|
+
headers: stringRecord(input.headers),
|
|
7088
|
+
body,
|
|
7089
|
+
body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : void 0,
|
|
7090
|
+
required: input.required === false ? false : true
|
|
7091
|
+
};
|
|
7092
|
+
}
|
|
7093
|
+
function normalizeNetworkMocks(value) {
|
|
7094
|
+
if (value === void 0) return void 0;
|
|
7095
|
+
if (!Array.isArray(value)) throw new Error("target.network_mocks must be an array.");
|
|
7096
|
+
return value.map(normalizeNetworkMock);
|
|
7097
|
+
}
|
|
7066
7098
|
function normalizeCheck(input, index) {
|
|
7067
7099
|
if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
7068
7100
|
const type = stringValue2(input.type);
|
|
@@ -7132,7 +7164,8 @@ function normalizeRiddleProofProfile(input, options = {}) {
|
|
|
7132
7164
|
timeout_sec: timeoutSecValue(targetInput.timeout_sec) ?? timeoutSecValue(targetInput.timeoutSec) ?? timeoutSecValue(targetInput.riddle_timeout_sec) ?? timeoutSecValue(targetInput.riddleTimeoutSec),
|
|
7133
7165
|
wait_for_selector: stringValue2(targetInput.wait_for_selector) || stringValue2(targetInput.waitForSelector),
|
|
7134
7166
|
wait_ms: numberValue(targetInput.wait_ms) ?? numberValue(targetInput.waitMs),
|
|
7135
|
-
setup_actions: normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions)
|
|
7167
|
+
setup_actions: normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions),
|
|
7168
|
+
network_mocks: normalizeNetworkMocks(targetInput.network_mocks ?? targetInput.networkMocks)
|
|
7136
7169
|
},
|
|
7137
7170
|
checks,
|
|
7138
7171
|
artifacts: Array.isArray(input.artifacts) ? input.artifacts.map((item) => String(item)).filter(Boolean) : ["screenshot", "console", "dom_summary", "proof_json"],
|
|
@@ -7404,6 +7437,50 @@ function assessSetupActionsFromEvidence(profile, evidence) {
|
|
|
7404
7437
|
message: failed.length ? `Setup actions failed in ${failed.length} viewport action(s).` : void 0
|
|
7405
7438
|
};
|
|
7406
7439
|
}
|
|
7440
|
+
function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
7441
|
+
const mocks = profile.target.network_mocks || [];
|
|
7442
|
+
if (!mocks.length) return void 0;
|
|
7443
|
+
const events = evidence.network_mocks || [];
|
|
7444
|
+
const failed = [];
|
|
7445
|
+
const requiredMocks = mocks.filter((mock) => mock.required !== false);
|
|
7446
|
+
for (const mock of requiredMocks) {
|
|
7447
|
+
const hits = events.filter((event) => event.label === mock.label && event.ok !== false).length;
|
|
7448
|
+
if (hits < 1) {
|
|
7449
|
+
failed.push({
|
|
7450
|
+
label: mock.label,
|
|
7451
|
+
url: mock.url,
|
|
7452
|
+
method: mock.method || null,
|
|
7453
|
+
reason: "required_mock_not_hit"
|
|
7454
|
+
});
|
|
7455
|
+
}
|
|
7456
|
+
}
|
|
7457
|
+
for (const event of events) {
|
|
7458
|
+
if (event.ok === false) {
|
|
7459
|
+
failed.push({
|
|
7460
|
+
label: event.label ?? null,
|
|
7461
|
+
url: event.url ?? null,
|
|
7462
|
+
method: event.method ?? null,
|
|
7463
|
+
reason: event.reason ?? event.error ?? "mock_failed"
|
|
7464
|
+
});
|
|
7465
|
+
}
|
|
7466
|
+
}
|
|
7467
|
+
return {
|
|
7468
|
+
type: "network_mocks_succeeded",
|
|
7469
|
+
label: "network mocks succeeded",
|
|
7470
|
+
status: failed.length ? "failed" : "passed",
|
|
7471
|
+
evidence: {
|
|
7472
|
+
mock_count: mocks.length,
|
|
7473
|
+
required_count: requiredMocks.length,
|
|
7474
|
+
hit_count: events.filter((event) => event.ok !== false).length,
|
|
7475
|
+
hits_by_label: Object.fromEntries(mocks.map((mock) => [
|
|
7476
|
+
mock.label,
|
|
7477
|
+
events.filter((event) => event.label === mock.label && event.ok !== false).length
|
|
7478
|
+
])),
|
|
7479
|
+
failed
|
|
7480
|
+
},
|
|
7481
|
+
message: failed.length ? `Network mocks failed or were not hit for ${failed.length} mock(s).` : void 0
|
|
7482
|
+
};
|
|
7483
|
+
}
|
|
7407
7484
|
function profileStatusFromEvidence(profile, evidence, checks) {
|
|
7408
7485
|
if (!evidence) return "proof_insufficient";
|
|
7409
7486
|
const viewports = evidence.viewports || [];
|
|
@@ -7418,6 +7495,7 @@ function profileStatusFromEvidence(profile, evidence, checks) {
|
|
|
7418
7495
|
function assessRiddleProofProfileEvidence(profile, evidence, options = {}) {
|
|
7419
7496
|
const capturedAt = evidence?.captured_at || (/* @__PURE__ */ new Date()).toISOString();
|
|
7420
7497
|
const checks = evidence ? [
|
|
7498
|
+
assessNetworkMocksFromEvidence(profile, evidence),
|
|
7421
7499
|
assessSetupActionsFromEvidence(profile, evidence),
|
|
7422
7500
|
...profile.checks.map((check) => assessCheckFromEvidence(check, evidence))
|
|
7423
7501
|
].filter((check) => Boolean(check)) : [];
|
|
@@ -7618,6 +7696,49 @@ function horizontalBoundsOverflowPx(value) {
|
|
|
7618
7696
|
function assessProfile(profile, evidence) {
|
|
7619
7697
|
const checks = [];
|
|
7620
7698
|
const viewports = evidence.viewports || [];
|
|
7699
|
+
if (profile.target && Array.isArray(profile.target.network_mocks) && profile.target.network_mocks.length) {
|
|
7700
|
+
const events = evidence.network_mocks || [];
|
|
7701
|
+
const requiredMocks = profile.target.network_mocks.filter((mock) => mock && mock.required !== false);
|
|
7702
|
+
const failed = [];
|
|
7703
|
+
for (const mock of requiredMocks) {
|
|
7704
|
+
const hits = events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
|
|
7705
|
+
if (hits < 1) {
|
|
7706
|
+
failed.push({
|
|
7707
|
+
label: mock.label,
|
|
7708
|
+
url: mock.url,
|
|
7709
|
+
method: mock.method || null,
|
|
7710
|
+
reason: "required_mock_not_hit",
|
|
7711
|
+
});
|
|
7712
|
+
}
|
|
7713
|
+
}
|
|
7714
|
+
for (const event of events) {
|
|
7715
|
+
if (event && event.ok === false) {
|
|
7716
|
+
failed.push({
|
|
7717
|
+
label: event.label || null,
|
|
7718
|
+
url: event.url || null,
|
|
7719
|
+
method: event.method || null,
|
|
7720
|
+
reason: event.reason || event.error || "mock_failed",
|
|
7721
|
+
});
|
|
7722
|
+
}
|
|
7723
|
+
}
|
|
7724
|
+
const hitsByLabel = {};
|
|
7725
|
+
for (const mock of profile.target.network_mocks) {
|
|
7726
|
+
hitsByLabel[mock.label] = events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
|
|
7727
|
+
}
|
|
7728
|
+
checks.push({
|
|
7729
|
+
type: "network_mocks_succeeded",
|
|
7730
|
+
label: "network mocks succeeded",
|
|
7731
|
+
status: failed.length ? "failed" : "passed",
|
|
7732
|
+
evidence: {
|
|
7733
|
+
mock_count: profile.target.network_mocks.length,
|
|
7734
|
+
required_count: requiredMocks.length,
|
|
7735
|
+
hit_count: events.filter((event) => event && event.ok !== false).length,
|
|
7736
|
+
hits_by_label: hitsByLabel,
|
|
7737
|
+
failed,
|
|
7738
|
+
},
|
|
7739
|
+
message: failed.length ? "Network mocks failed or were not hit for " + failed.length + " mock(s)." : undefined,
|
|
7740
|
+
});
|
|
7741
|
+
}
|
|
7621
7742
|
if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
|
|
7622
7743
|
const actionCount = profile.target.setup_actions.length;
|
|
7623
7744
|
const failed = [];
|
|
@@ -7807,6 +7928,7 @@ const profileSlug = ${serializableSlug};
|
|
|
7807
7928
|
const capturedAt = new Date().toISOString();
|
|
7808
7929
|
const consoleEvents = [];
|
|
7809
7930
|
const pageErrors = [];
|
|
7931
|
+
const networkMockEvents = [];
|
|
7810
7932
|
page.on("console", (message) => {
|
|
7811
7933
|
const type = message.type();
|
|
7812
7934
|
if (type === "error" || type === "warning" || type === "assert") {
|
|
@@ -7853,6 +7975,49 @@ function compactSetupResultText(value) {
|
|
|
7853
7975
|
async function setupLocatorVisible(locator, index) {
|
|
7854
7976
|
return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
|
|
7855
7977
|
}
|
|
7978
|
+
async function registerNetworkMocks(mocks) {
|
|
7979
|
+
for (const mock of mocks || []) {
|
|
7980
|
+
await page.route(mock.url, async (route) => {
|
|
7981
|
+
const request = route.request();
|
|
7982
|
+
const method = request.method ? request.method() : "";
|
|
7983
|
+
if (mock.method && method.toUpperCase() !== String(mock.method).toUpperCase()) {
|
|
7984
|
+
await route.continue();
|
|
7985
|
+
return;
|
|
7986
|
+
}
|
|
7987
|
+
try {
|
|
7988
|
+
const headers = { ...(mock.headers || {}) };
|
|
7989
|
+
let body = mock.body || "";
|
|
7990
|
+
let contentType = mock.content_type || headers["content-type"] || headers["Content-Type"] || "text/plain";
|
|
7991
|
+
if (mock.body_json !== undefined) {
|
|
7992
|
+
body = JSON.stringify(mock.body_json);
|
|
7993
|
+
contentType = mock.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
|
|
7994
|
+
}
|
|
7995
|
+
networkMockEvents.push({
|
|
7996
|
+
ok: true,
|
|
7997
|
+
label: mock.label,
|
|
7998
|
+
url: request.url(),
|
|
7999
|
+
method,
|
|
8000
|
+
status: mock.status || 200,
|
|
8001
|
+
});
|
|
8002
|
+
await route.fulfill({
|
|
8003
|
+
status: mock.status || 200,
|
|
8004
|
+
headers,
|
|
8005
|
+
contentType,
|
|
8006
|
+
body,
|
|
8007
|
+
});
|
|
8008
|
+
} catch (error) {
|
|
8009
|
+
networkMockEvents.push({
|
|
8010
|
+
ok: false,
|
|
8011
|
+
label: mock.label,
|
|
8012
|
+
url: request.url(),
|
|
8013
|
+
method,
|
|
8014
|
+
error: String(error && error.message ? error.message : error).slice(0, 1000),
|
|
8015
|
+
});
|
|
8016
|
+
throw error;
|
|
8017
|
+
}
|
|
8018
|
+
});
|
|
8019
|
+
}
|
|
8020
|
+
}
|
|
7856
8021
|
async function executeSetupAction(action, ordinal) {
|
|
7857
8022
|
const type = setupActionType(action);
|
|
7858
8023
|
const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null };
|
|
@@ -8114,6 +8279,7 @@ function buildProfileEvidence(currentViewports) {
|
|
|
8114
8279
|
fatal_count: consoleEvents.filter((event) => event.type === "error" || event.type === "assert").length,
|
|
8115
8280
|
},
|
|
8116
8281
|
page_errors: pageErrors,
|
|
8282
|
+
network_mocks: networkMockEvents.slice(),
|
|
8117
8283
|
dom_summary: {
|
|
8118
8284
|
expected_viewport_count: expectedViewportCount,
|
|
8119
8285
|
viewport_count: currentViewports.length,
|
|
@@ -8123,6 +8289,8 @@ function buildProfileEvidence(currentViewports) {
|
|
|
8123
8289
|
overflow_px: currentViewports.map((viewport) => viewport.overflow_px),
|
|
8124
8290
|
bounds_overflow_px: currentViewports.map((viewport) => viewport.bounds_overflow_px),
|
|
8125
8291
|
overflow_offender_counts: currentViewports.map((viewport) => (viewport.overflow_offenders || []).length),
|
|
8292
|
+
network_mock_count: (profile.target.network_mocks || []).length,
|
|
8293
|
+
network_mock_hit_count: networkMockEvents.filter((event) => event.ok !== false).length,
|
|
8126
8294
|
},
|
|
8127
8295
|
};
|
|
8128
8296
|
}
|
|
@@ -8136,6 +8304,9 @@ async function saveProfileArtifacts(currentViewports) {
|
|
|
8136
8304
|
}
|
|
8137
8305
|
return result;
|
|
8138
8306
|
}
|
|
8307
|
+
await registerNetworkMocks(profile.target.network_mocks || []);
|
|
8308
|
+
consoleEvents.length = 0;
|
|
8309
|
+
pageErrors.length = 0;
|
|
8139
8310
|
let result = await saveProfileArtifacts(viewports);
|
|
8140
8311
|
for (const viewport of profile.target.viewports || []) {
|
|
8141
8312
|
viewports.push(await captureViewport(viewport));
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -8833,6 +8833,11 @@ function jsonRecord(value) {
|
|
|
8833
8833
|
if (!isRecord2(value)) return void 0;
|
|
8834
8834
|
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
|
|
8835
8835
|
}
|
|
8836
|
+
function stringRecord(value) {
|
|
8837
|
+
if (!isRecord2(value)) return void 0;
|
|
8838
|
+
const entries = Object.entries(value).map(([key, child]) => [key, String(child)]).filter(([key]) => key.trim());
|
|
8839
|
+
return entries.length ? Object.fromEntries(entries) : void 0;
|
|
8840
|
+
}
|
|
8836
8841
|
function toJsonValue(value) {
|
|
8837
8842
|
if (value === null || value === void 0) return null;
|
|
8838
8843
|
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
@@ -8904,6 +8909,33 @@ function normalizeSetupActions(value) {
|
|
|
8904
8909
|
if (!Array.isArray(value)) throw new Error("target.setup_actions must be an array.");
|
|
8905
8910
|
return value.map(normalizeSetupAction);
|
|
8906
8911
|
}
|
|
8912
|
+
function normalizeNetworkMock(input, index) {
|
|
8913
|
+
if (!isRecord2(input)) throw new Error(`target.network_mocks[${index}] must be an object.`);
|
|
8914
|
+
const url = stringValue5(input.url) || stringValue5(input.glob) || stringValue5(input.pattern);
|
|
8915
|
+
if (!url) throw new Error(`target.network_mocks[${index}] requires url.`);
|
|
8916
|
+
const status = numberValue3(input.status) ?? 200;
|
|
8917
|
+
if (!Number.isInteger(status) || status < 100 || status > 599) {
|
|
8918
|
+
throw new Error(`target.network_mocks[${index}].status must be an HTTP status code.`);
|
|
8919
|
+
}
|
|
8920
|
+
const body = stringValue5(input.body) ?? stringValue5(input.body_text) ?? stringValue5(input.bodyText);
|
|
8921
|
+
const hasJsonBody = Object.prototype.hasOwnProperty.call(input, "body_json") || Object.prototype.hasOwnProperty.call(input, "bodyJson") || Object.prototype.hasOwnProperty.call(input, "json");
|
|
8922
|
+
return {
|
|
8923
|
+
label: normalizeName(input.label || input.name, `network-mock-${index + 1}`),
|
|
8924
|
+
url,
|
|
8925
|
+
method: stringValue5(input.method)?.toUpperCase(),
|
|
8926
|
+
status,
|
|
8927
|
+
content_type: stringValue5(input.content_type) || stringValue5(input.contentType),
|
|
8928
|
+
headers: stringRecord(input.headers),
|
|
8929
|
+
body,
|
|
8930
|
+
body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : void 0,
|
|
8931
|
+
required: input.required === false ? false : true
|
|
8932
|
+
};
|
|
8933
|
+
}
|
|
8934
|
+
function normalizeNetworkMocks(value) {
|
|
8935
|
+
if (value === void 0) return void 0;
|
|
8936
|
+
if (!Array.isArray(value)) throw new Error("target.network_mocks must be an array.");
|
|
8937
|
+
return value.map(normalizeNetworkMock);
|
|
8938
|
+
}
|
|
8907
8939
|
function normalizeCheck(input, index) {
|
|
8908
8940
|
if (!isRecord2(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
8909
8941
|
const type = stringValue5(input.type);
|
|
@@ -8973,7 +9005,8 @@ function normalizeRiddleProofProfile(input, options = {}) {
|
|
|
8973
9005
|
timeout_sec: timeoutSecValue(targetInput.timeout_sec) ?? timeoutSecValue(targetInput.timeoutSec) ?? timeoutSecValue(targetInput.riddle_timeout_sec) ?? timeoutSecValue(targetInput.riddleTimeoutSec),
|
|
8974
9006
|
wait_for_selector: stringValue5(targetInput.wait_for_selector) || stringValue5(targetInput.waitForSelector),
|
|
8975
9007
|
wait_ms: numberValue3(targetInput.wait_ms) ?? numberValue3(targetInput.waitMs),
|
|
8976
|
-
setup_actions: normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions)
|
|
9008
|
+
setup_actions: normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions),
|
|
9009
|
+
network_mocks: normalizeNetworkMocks(targetInput.network_mocks ?? targetInput.networkMocks)
|
|
8977
9010
|
},
|
|
8978
9011
|
checks,
|
|
8979
9012
|
artifacts: Array.isArray(input.artifacts) ? input.artifacts.map((item) => String(item)).filter(Boolean) : ["screenshot", "console", "dom_summary", "proof_json"],
|
|
@@ -9245,6 +9278,50 @@ function assessSetupActionsFromEvidence(profile, evidence) {
|
|
|
9245
9278
|
message: failed.length ? `Setup actions failed in ${failed.length} viewport action(s).` : void 0
|
|
9246
9279
|
};
|
|
9247
9280
|
}
|
|
9281
|
+
function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
9282
|
+
const mocks = profile.target.network_mocks || [];
|
|
9283
|
+
if (!mocks.length) return void 0;
|
|
9284
|
+
const events = evidence.network_mocks || [];
|
|
9285
|
+
const failed = [];
|
|
9286
|
+
const requiredMocks = mocks.filter((mock) => mock.required !== false);
|
|
9287
|
+
for (const mock of requiredMocks) {
|
|
9288
|
+
const hits = events.filter((event) => event.label === mock.label && event.ok !== false).length;
|
|
9289
|
+
if (hits < 1) {
|
|
9290
|
+
failed.push({
|
|
9291
|
+
label: mock.label,
|
|
9292
|
+
url: mock.url,
|
|
9293
|
+
method: mock.method || null,
|
|
9294
|
+
reason: "required_mock_not_hit"
|
|
9295
|
+
});
|
|
9296
|
+
}
|
|
9297
|
+
}
|
|
9298
|
+
for (const event of events) {
|
|
9299
|
+
if (event.ok === false) {
|
|
9300
|
+
failed.push({
|
|
9301
|
+
label: event.label ?? null,
|
|
9302
|
+
url: event.url ?? null,
|
|
9303
|
+
method: event.method ?? null,
|
|
9304
|
+
reason: event.reason ?? event.error ?? "mock_failed"
|
|
9305
|
+
});
|
|
9306
|
+
}
|
|
9307
|
+
}
|
|
9308
|
+
return {
|
|
9309
|
+
type: "network_mocks_succeeded",
|
|
9310
|
+
label: "network mocks succeeded",
|
|
9311
|
+
status: failed.length ? "failed" : "passed",
|
|
9312
|
+
evidence: {
|
|
9313
|
+
mock_count: mocks.length,
|
|
9314
|
+
required_count: requiredMocks.length,
|
|
9315
|
+
hit_count: events.filter((event) => event.ok !== false).length,
|
|
9316
|
+
hits_by_label: Object.fromEntries(mocks.map((mock) => [
|
|
9317
|
+
mock.label,
|
|
9318
|
+
events.filter((event) => event.label === mock.label && event.ok !== false).length
|
|
9319
|
+
])),
|
|
9320
|
+
failed
|
|
9321
|
+
},
|
|
9322
|
+
message: failed.length ? `Network mocks failed or were not hit for ${failed.length} mock(s).` : void 0
|
|
9323
|
+
};
|
|
9324
|
+
}
|
|
9248
9325
|
function profileStatusFromEvidence(profile, evidence, checks) {
|
|
9249
9326
|
if (!evidence) return "proof_insufficient";
|
|
9250
9327
|
const viewports = evidence.viewports || [];
|
|
@@ -9259,6 +9336,7 @@ function profileStatusFromEvidence(profile, evidence, checks) {
|
|
|
9259
9336
|
function assessRiddleProofProfileEvidence(profile, evidence, options = {}) {
|
|
9260
9337
|
const capturedAt = evidence?.captured_at || (/* @__PURE__ */ new Date()).toISOString();
|
|
9261
9338
|
const checks = evidence ? [
|
|
9339
|
+
assessNetworkMocksFromEvidence(profile, evidence),
|
|
9262
9340
|
assessSetupActionsFromEvidence(profile, evidence),
|
|
9263
9341
|
...profile.checks.map((check) => assessCheckFromEvidence(check, evidence))
|
|
9264
9342
|
].filter((check) => Boolean(check)) : [];
|
|
@@ -9475,6 +9553,49 @@ function horizontalBoundsOverflowPx(value) {
|
|
|
9475
9553
|
function assessProfile(profile, evidence) {
|
|
9476
9554
|
const checks = [];
|
|
9477
9555
|
const viewports = evidence.viewports || [];
|
|
9556
|
+
if (profile.target && Array.isArray(profile.target.network_mocks) && profile.target.network_mocks.length) {
|
|
9557
|
+
const events = evidence.network_mocks || [];
|
|
9558
|
+
const requiredMocks = profile.target.network_mocks.filter((mock) => mock && mock.required !== false);
|
|
9559
|
+
const failed = [];
|
|
9560
|
+
for (const mock of requiredMocks) {
|
|
9561
|
+
const hits = events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
|
|
9562
|
+
if (hits < 1) {
|
|
9563
|
+
failed.push({
|
|
9564
|
+
label: mock.label,
|
|
9565
|
+
url: mock.url,
|
|
9566
|
+
method: mock.method || null,
|
|
9567
|
+
reason: "required_mock_not_hit",
|
|
9568
|
+
});
|
|
9569
|
+
}
|
|
9570
|
+
}
|
|
9571
|
+
for (const event of events) {
|
|
9572
|
+
if (event && event.ok === false) {
|
|
9573
|
+
failed.push({
|
|
9574
|
+
label: event.label || null,
|
|
9575
|
+
url: event.url || null,
|
|
9576
|
+
method: event.method || null,
|
|
9577
|
+
reason: event.reason || event.error || "mock_failed",
|
|
9578
|
+
});
|
|
9579
|
+
}
|
|
9580
|
+
}
|
|
9581
|
+
const hitsByLabel = {};
|
|
9582
|
+
for (const mock of profile.target.network_mocks) {
|
|
9583
|
+
hitsByLabel[mock.label] = events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
|
|
9584
|
+
}
|
|
9585
|
+
checks.push({
|
|
9586
|
+
type: "network_mocks_succeeded",
|
|
9587
|
+
label: "network mocks succeeded",
|
|
9588
|
+
status: failed.length ? "failed" : "passed",
|
|
9589
|
+
evidence: {
|
|
9590
|
+
mock_count: profile.target.network_mocks.length,
|
|
9591
|
+
required_count: requiredMocks.length,
|
|
9592
|
+
hit_count: events.filter((event) => event && event.ok !== false).length,
|
|
9593
|
+
hits_by_label: hitsByLabel,
|
|
9594
|
+
failed,
|
|
9595
|
+
},
|
|
9596
|
+
message: failed.length ? "Network mocks failed or were not hit for " + failed.length + " mock(s)." : undefined,
|
|
9597
|
+
});
|
|
9598
|
+
}
|
|
9478
9599
|
if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
|
|
9479
9600
|
const actionCount = profile.target.setup_actions.length;
|
|
9480
9601
|
const failed = [];
|
|
@@ -9664,6 +9785,7 @@ const profileSlug = ${serializableSlug};
|
|
|
9664
9785
|
const capturedAt = new Date().toISOString();
|
|
9665
9786
|
const consoleEvents = [];
|
|
9666
9787
|
const pageErrors = [];
|
|
9788
|
+
const networkMockEvents = [];
|
|
9667
9789
|
page.on("console", (message) => {
|
|
9668
9790
|
const type = message.type();
|
|
9669
9791
|
if (type === "error" || type === "warning" || type === "assert") {
|
|
@@ -9710,6 +9832,49 @@ function compactSetupResultText(value) {
|
|
|
9710
9832
|
async function setupLocatorVisible(locator, index) {
|
|
9711
9833
|
return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
|
|
9712
9834
|
}
|
|
9835
|
+
async function registerNetworkMocks(mocks) {
|
|
9836
|
+
for (const mock of mocks || []) {
|
|
9837
|
+
await page.route(mock.url, async (route) => {
|
|
9838
|
+
const request = route.request();
|
|
9839
|
+
const method = request.method ? request.method() : "";
|
|
9840
|
+
if (mock.method && method.toUpperCase() !== String(mock.method).toUpperCase()) {
|
|
9841
|
+
await route.continue();
|
|
9842
|
+
return;
|
|
9843
|
+
}
|
|
9844
|
+
try {
|
|
9845
|
+
const headers = { ...(mock.headers || {}) };
|
|
9846
|
+
let body = mock.body || "";
|
|
9847
|
+
let contentType = mock.content_type || headers["content-type"] || headers["Content-Type"] || "text/plain";
|
|
9848
|
+
if (mock.body_json !== undefined) {
|
|
9849
|
+
body = JSON.stringify(mock.body_json);
|
|
9850
|
+
contentType = mock.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
|
|
9851
|
+
}
|
|
9852
|
+
networkMockEvents.push({
|
|
9853
|
+
ok: true,
|
|
9854
|
+
label: mock.label,
|
|
9855
|
+
url: request.url(),
|
|
9856
|
+
method,
|
|
9857
|
+
status: mock.status || 200,
|
|
9858
|
+
});
|
|
9859
|
+
await route.fulfill({
|
|
9860
|
+
status: mock.status || 200,
|
|
9861
|
+
headers,
|
|
9862
|
+
contentType,
|
|
9863
|
+
body,
|
|
9864
|
+
});
|
|
9865
|
+
} catch (error) {
|
|
9866
|
+
networkMockEvents.push({
|
|
9867
|
+
ok: false,
|
|
9868
|
+
label: mock.label,
|
|
9869
|
+
url: request.url(),
|
|
9870
|
+
method,
|
|
9871
|
+
error: String(error && error.message ? error.message : error).slice(0, 1000),
|
|
9872
|
+
});
|
|
9873
|
+
throw error;
|
|
9874
|
+
}
|
|
9875
|
+
});
|
|
9876
|
+
}
|
|
9877
|
+
}
|
|
9713
9878
|
async function executeSetupAction(action, ordinal) {
|
|
9714
9879
|
const type = setupActionType(action);
|
|
9715
9880
|
const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null };
|
|
@@ -9971,6 +10136,7 @@ function buildProfileEvidence(currentViewports) {
|
|
|
9971
10136
|
fatal_count: consoleEvents.filter((event) => event.type === "error" || event.type === "assert").length,
|
|
9972
10137
|
},
|
|
9973
10138
|
page_errors: pageErrors,
|
|
10139
|
+
network_mocks: networkMockEvents.slice(),
|
|
9974
10140
|
dom_summary: {
|
|
9975
10141
|
expected_viewport_count: expectedViewportCount,
|
|
9976
10142
|
viewport_count: currentViewports.length,
|
|
@@ -9980,6 +10146,8 @@ function buildProfileEvidence(currentViewports) {
|
|
|
9980
10146
|
overflow_px: currentViewports.map((viewport) => viewport.overflow_px),
|
|
9981
10147
|
bounds_overflow_px: currentViewports.map((viewport) => viewport.bounds_overflow_px),
|
|
9982
10148
|
overflow_offender_counts: currentViewports.map((viewport) => (viewport.overflow_offenders || []).length),
|
|
10149
|
+
network_mock_count: (profile.target.network_mocks || []).length,
|
|
10150
|
+
network_mock_hit_count: networkMockEvents.filter((event) => event.ok !== false).length,
|
|
9983
10151
|
},
|
|
9984
10152
|
};
|
|
9985
10153
|
}
|
|
@@ -9993,6 +10161,9 @@ async function saveProfileArtifacts(currentViewports) {
|
|
|
9993
10161
|
}
|
|
9994
10162
|
return result;
|
|
9995
10163
|
}
|
|
10164
|
+
await registerNetworkMocks(profile.target.network_mocks || []);
|
|
10165
|
+
consoleEvents.length = 0;
|
|
10166
|
+
pageErrors.length = 0;
|
|
9996
10167
|
let result = await saveProfileArtifacts(viewports);
|
|
9997
10168
|
for (const viewport of profile.target.viewports || []) {
|
|
9998
10169
|
viewports.push(await captureViewport(viewport));
|
package/dist/index.d.cts
CHANGED
|
@@ -10,5 +10,5 @@ export { CreateCaptureDiagnosticInput, DEFAULT_DIAGNOSTIC_ARRAY_LIMIT, DEFAULT_D
|
|
|
10
10
|
export { BuildVisualProofSessionInput, RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_VERSION, RIDDLE_PROOF_VISUAL_SESSION_VERSION, VisualProofSessionMismatch, buildVisualProofSession, compareVisualProofSessionFingerprint, parseVisualProofSession, visualSessionFingerprint, visualSessionFingerprintBasis } from './proof-session.cjs';
|
|
11
11
|
export { AssessPlayabilityOptions, RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION, RIDDLE_PROOF_PLAYABILITY_VERSION, RiddleProofPlayabilityAssessment, RiddleProofPlayabilityEvidence, assessPlayabilityEvidence, extractPlayabilityEvidence, isRiddleProofPlayabilityMode } from './playability.cjs';
|
|
12
12
|
export { AssessBasicGameplayOptions, AttachBasicGameplayArtifactOptions, BASIC_GAMEPLAY_ACTION_TYPES, BASIC_GAMEPLAY_PROGRESS_CHECK_TYPES, BasicGameplayActionResult, BasicGameplayActionType, BasicGameplayArtifactResolution, BasicGameplayAssessmentSummary, BasicGameplayBoundsOffender, BasicGameplayCanvasState, BasicGameplayCatchRecord, BasicGameplayChangeSummary, BasicGameplayFailureCode, BasicGameplayFixReference, BasicGameplayMetric, BasicGameplayMobileEvidence, BasicGameplayProgressCheckType, BasicGameplayProgressionCheck, BasicGameplayProofArtifact, BasicGameplayResponsiveViewportEvidence, BasicGameplayRouteReference, BasicGameplaySnapshot, BasicGameplaySuiteFailure, BasicGameplayWarningCode, CreateBasicGameplayCatchSummaryInput, RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION, RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION, RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION, RiddleProofBasicGameplayAssessment, RiddleProofBasicGameplayCatchSummary, RiddleProofBasicGameplayEvidence, RiddleProofBasicGameplayRouteAssessment, RiddleProofBasicGameplayRouteEvidence, assessBasicGameplayEvidence, assessBasicGameplayProgressionCheck, assessBasicGameplayProgressionChecks, assessBasicGameplayRoute, attachBasicGameplayArtifactScreenshotHashes, augmentBasicGameplayAssessmentWithProgressionChecks, compactBasicGameplayText, createBasicGameplayCatchRecords, createBasicGameplayCatchSummary, extractBasicGameplayEvidence, resolveBasicGameplayProgressionCheckWithArtifactScreenshots, sanitizeBasicGameplayJsonString } from './basic-gameplay.cjs';
|
|
13
|
-
export { NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, RiddleProofProfile, RiddleProofProfileArtifactRef, RiddleProofProfileBaselinePolicy, RiddleProofProfileBoundsOffender, RiddleProofProfileCheck, RiddleProofProfileCheckResult, RiddleProofProfileCheckType, RiddleProofProfileEvidence, RiddleProofProfileFailureAction, RiddleProofProfileResult, RiddleProofProfileRouteEvidence, RiddleProofProfileRunner, RiddleProofProfileSetupAction, RiddleProofProfileSetupActionType, RiddleProofProfileStatus, RiddleProofProfileTarget, RiddleProofProfileViewport, RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, extractRiddleProofProfileResult, normalizeRiddleProofProfile, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult } from './profile.cjs';
|
|
13
|
+
export { NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, RiddleProofProfile, RiddleProofProfileArtifactRef, RiddleProofProfileBaselinePolicy, RiddleProofProfileBoundsOffender, RiddleProofProfileCheck, RiddleProofProfileCheckResult, RiddleProofProfileCheckType, RiddleProofProfileEvidence, RiddleProofProfileFailureAction, RiddleProofProfileNetworkMock, RiddleProofProfileResult, RiddleProofProfileRouteEvidence, RiddleProofProfileRunner, RiddleProofProfileSetupAction, RiddleProofProfileSetupActionType, RiddleProofProfileStatus, RiddleProofProfileTarget, RiddleProofProfileViewport, RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, extractRiddleProofProfileResult, normalizeRiddleProofProfile, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult } from './profile.cjs';
|
|
14
14
|
export { DEFAULT_RIDDLE_API_BASE_URL, DEFAULT_RIDDLE_API_KEY_FILE, RiddleApiError, RiddleClientConfig, RiddleFetch, RiddlePollJobOptions, RiddlePollJobResult, RiddlePollProgressSnapshot, RiddlePollSummary, RiddlePreviewDeployResult, RiddleRunScriptInput, RiddleServerPreviewInput, RiddleServerPreviewResult, createRiddleApiClient, deployRiddleStaticPreview, isTerminalRiddleJobStatus, parseRiddleViewport, pollRiddleJob, resolveRiddleApiKey, riddleRequestJson, runRiddleScript, runRiddleServerPreview } from './riddle-client.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ export { CreateCaptureDiagnosticInput, DEFAULT_DIAGNOSTIC_ARRAY_LIMIT, DEFAULT_D
|
|
|
10
10
|
export { BuildVisualProofSessionInput, RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_VERSION, RIDDLE_PROOF_VISUAL_SESSION_VERSION, VisualProofSessionMismatch, buildVisualProofSession, compareVisualProofSessionFingerprint, parseVisualProofSession, visualSessionFingerprint, visualSessionFingerprintBasis } from './proof-session.js';
|
|
11
11
|
export { AssessPlayabilityOptions, RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION, RIDDLE_PROOF_PLAYABILITY_VERSION, RiddleProofPlayabilityAssessment, RiddleProofPlayabilityEvidence, assessPlayabilityEvidence, extractPlayabilityEvidence, isRiddleProofPlayabilityMode } from './playability.js';
|
|
12
12
|
export { AssessBasicGameplayOptions, AttachBasicGameplayArtifactOptions, BASIC_GAMEPLAY_ACTION_TYPES, BASIC_GAMEPLAY_PROGRESS_CHECK_TYPES, BasicGameplayActionResult, BasicGameplayActionType, BasicGameplayArtifactResolution, BasicGameplayAssessmentSummary, BasicGameplayBoundsOffender, BasicGameplayCanvasState, BasicGameplayCatchRecord, BasicGameplayChangeSummary, BasicGameplayFailureCode, BasicGameplayFixReference, BasicGameplayMetric, BasicGameplayMobileEvidence, BasicGameplayProgressCheckType, BasicGameplayProgressionCheck, BasicGameplayProofArtifact, BasicGameplayResponsiveViewportEvidence, BasicGameplayRouteReference, BasicGameplaySnapshot, BasicGameplaySuiteFailure, BasicGameplayWarningCode, CreateBasicGameplayCatchSummaryInput, RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION, RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION, RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION, RiddleProofBasicGameplayAssessment, RiddleProofBasicGameplayCatchSummary, RiddleProofBasicGameplayEvidence, RiddleProofBasicGameplayRouteAssessment, RiddleProofBasicGameplayRouteEvidence, assessBasicGameplayEvidence, assessBasicGameplayProgressionCheck, assessBasicGameplayProgressionChecks, assessBasicGameplayRoute, attachBasicGameplayArtifactScreenshotHashes, augmentBasicGameplayAssessmentWithProgressionChecks, compactBasicGameplayText, createBasicGameplayCatchRecords, createBasicGameplayCatchSummary, extractBasicGameplayEvidence, resolveBasicGameplayProgressionCheckWithArtifactScreenshots, sanitizeBasicGameplayJsonString } from './basic-gameplay.js';
|
|
13
|
-
export { NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, RiddleProofProfile, RiddleProofProfileArtifactRef, RiddleProofProfileBaselinePolicy, RiddleProofProfileBoundsOffender, RiddleProofProfileCheck, RiddleProofProfileCheckResult, RiddleProofProfileCheckType, RiddleProofProfileEvidence, RiddleProofProfileFailureAction, RiddleProofProfileResult, RiddleProofProfileRouteEvidence, RiddleProofProfileRunner, RiddleProofProfileSetupAction, RiddleProofProfileSetupActionType, RiddleProofProfileStatus, RiddleProofProfileTarget, RiddleProofProfileViewport, RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, extractRiddleProofProfileResult, normalizeRiddleProofProfile, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult } from './profile.js';
|
|
13
|
+
export { NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, RiddleProofProfile, RiddleProofProfileArtifactRef, RiddleProofProfileBaselinePolicy, RiddleProofProfileBoundsOffender, RiddleProofProfileCheck, RiddleProofProfileCheckResult, RiddleProofProfileCheckType, RiddleProofProfileEvidence, RiddleProofProfileFailureAction, RiddleProofProfileNetworkMock, RiddleProofProfileResult, RiddleProofProfileRouteEvidence, RiddleProofProfileRunner, RiddleProofProfileSetupAction, RiddleProofProfileSetupActionType, RiddleProofProfileStatus, RiddleProofProfileTarget, RiddleProofProfileViewport, RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, extractRiddleProofProfileResult, normalizeRiddleProofProfile, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult } from './profile.js';
|
|
14
14
|
export { DEFAULT_RIDDLE_API_BASE_URL, DEFAULT_RIDDLE_API_KEY_FILE, RiddleApiError, RiddleClientConfig, RiddleFetch, RiddlePollJobOptions, RiddlePollJobResult, RiddlePollProgressSnapshot, RiddlePollSummary, RiddlePreviewDeployResult, RiddleRunScriptInput, RiddleServerPreviewInput, RiddleServerPreviewResult, createRiddleApiClient, deployRiddleStaticPreview, isTerminalRiddleJobStatus, parseRiddleViewport, pollRiddleJob, resolveRiddleApiKey, riddleRequestJson, runRiddleScript, runRiddleServerPreview } from './riddle-client.js';
|
package/dist/index.js
CHANGED
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
resolveRiddleProofProfileTimeoutSec,
|
|
59
59
|
slugifyRiddleProofProfileName,
|
|
60
60
|
summarizeRiddleProofProfileResult
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-FUWINYNX.js";
|
|
62
62
|
import {
|
|
63
63
|
DEFAULT_RIDDLE_API_BASE_URL,
|
|
64
64
|
DEFAULT_RIDDLE_API_KEY_FILE,
|
package/dist/profile.cjs
CHANGED
|
@@ -162,6 +162,11 @@ function jsonRecord(value) {
|
|
|
162
162
|
if (!isRecord(value)) return void 0;
|
|
163
163
|
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
|
|
164
164
|
}
|
|
165
|
+
function stringRecord(value) {
|
|
166
|
+
if (!isRecord(value)) return void 0;
|
|
167
|
+
const entries = Object.entries(value).map(([key, child]) => [key, String(child)]).filter(([key]) => key.trim());
|
|
168
|
+
return entries.length ? Object.fromEntries(entries) : void 0;
|
|
169
|
+
}
|
|
165
170
|
function toJsonValue(value) {
|
|
166
171
|
if (value === null || value === void 0) return null;
|
|
167
172
|
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
@@ -233,6 +238,33 @@ function normalizeSetupActions(value) {
|
|
|
233
238
|
if (!Array.isArray(value)) throw new Error("target.setup_actions must be an array.");
|
|
234
239
|
return value.map(normalizeSetupAction);
|
|
235
240
|
}
|
|
241
|
+
function normalizeNetworkMock(input, index) {
|
|
242
|
+
if (!isRecord(input)) throw new Error(`target.network_mocks[${index}] must be an object.`);
|
|
243
|
+
const url = stringValue(input.url) || stringValue(input.glob) || stringValue(input.pattern);
|
|
244
|
+
if (!url) throw new Error(`target.network_mocks[${index}] requires url.`);
|
|
245
|
+
const status = numberValue(input.status) ?? 200;
|
|
246
|
+
if (!Number.isInteger(status) || status < 100 || status > 599) {
|
|
247
|
+
throw new Error(`target.network_mocks[${index}].status must be an HTTP status code.`);
|
|
248
|
+
}
|
|
249
|
+
const body = stringValue(input.body) ?? stringValue(input.body_text) ?? stringValue(input.bodyText);
|
|
250
|
+
const hasJsonBody = Object.prototype.hasOwnProperty.call(input, "body_json") || Object.prototype.hasOwnProperty.call(input, "bodyJson") || Object.prototype.hasOwnProperty.call(input, "json");
|
|
251
|
+
return {
|
|
252
|
+
label: normalizeName(input.label || input.name, `network-mock-${index + 1}`),
|
|
253
|
+
url,
|
|
254
|
+
method: stringValue(input.method)?.toUpperCase(),
|
|
255
|
+
status,
|
|
256
|
+
content_type: stringValue(input.content_type) || stringValue(input.contentType),
|
|
257
|
+
headers: stringRecord(input.headers),
|
|
258
|
+
body,
|
|
259
|
+
body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : void 0,
|
|
260
|
+
required: input.required === false ? false : true
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function normalizeNetworkMocks(value) {
|
|
264
|
+
if (value === void 0) return void 0;
|
|
265
|
+
if (!Array.isArray(value)) throw new Error("target.network_mocks must be an array.");
|
|
266
|
+
return value.map(normalizeNetworkMock);
|
|
267
|
+
}
|
|
236
268
|
function normalizeCheck(input, index) {
|
|
237
269
|
if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
238
270
|
const type = stringValue(input.type);
|
|
@@ -302,7 +334,8 @@ function normalizeRiddleProofProfile(input, options = {}) {
|
|
|
302
334
|
timeout_sec: timeoutSecValue(targetInput.timeout_sec) ?? timeoutSecValue(targetInput.timeoutSec) ?? timeoutSecValue(targetInput.riddle_timeout_sec) ?? timeoutSecValue(targetInput.riddleTimeoutSec),
|
|
303
335
|
wait_for_selector: stringValue(targetInput.wait_for_selector) || stringValue(targetInput.waitForSelector),
|
|
304
336
|
wait_ms: numberValue(targetInput.wait_ms) ?? numberValue(targetInput.waitMs),
|
|
305
|
-
setup_actions: normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions)
|
|
337
|
+
setup_actions: normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions),
|
|
338
|
+
network_mocks: normalizeNetworkMocks(targetInput.network_mocks ?? targetInput.networkMocks)
|
|
306
339
|
},
|
|
307
340
|
checks,
|
|
308
341
|
artifacts: Array.isArray(input.artifacts) ? input.artifacts.map((item) => String(item)).filter(Boolean) : ["screenshot", "console", "dom_summary", "proof_json"],
|
|
@@ -574,6 +607,50 @@ function assessSetupActionsFromEvidence(profile, evidence) {
|
|
|
574
607
|
message: failed.length ? `Setup actions failed in ${failed.length} viewport action(s).` : void 0
|
|
575
608
|
};
|
|
576
609
|
}
|
|
610
|
+
function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
611
|
+
const mocks = profile.target.network_mocks || [];
|
|
612
|
+
if (!mocks.length) return void 0;
|
|
613
|
+
const events = evidence.network_mocks || [];
|
|
614
|
+
const failed = [];
|
|
615
|
+
const requiredMocks = mocks.filter((mock) => mock.required !== false);
|
|
616
|
+
for (const mock of requiredMocks) {
|
|
617
|
+
const hits = events.filter((event) => event.label === mock.label && event.ok !== false).length;
|
|
618
|
+
if (hits < 1) {
|
|
619
|
+
failed.push({
|
|
620
|
+
label: mock.label,
|
|
621
|
+
url: mock.url,
|
|
622
|
+
method: mock.method || null,
|
|
623
|
+
reason: "required_mock_not_hit"
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
for (const event of events) {
|
|
628
|
+
if (event.ok === false) {
|
|
629
|
+
failed.push({
|
|
630
|
+
label: event.label ?? null,
|
|
631
|
+
url: event.url ?? null,
|
|
632
|
+
method: event.method ?? null,
|
|
633
|
+
reason: event.reason ?? event.error ?? "mock_failed"
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return {
|
|
638
|
+
type: "network_mocks_succeeded",
|
|
639
|
+
label: "network mocks succeeded",
|
|
640
|
+
status: failed.length ? "failed" : "passed",
|
|
641
|
+
evidence: {
|
|
642
|
+
mock_count: mocks.length,
|
|
643
|
+
required_count: requiredMocks.length,
|
|
644
|
+
hit_count: events.filter((event) => event.ok !== false).length,
|
|
645
|
+
hits_by_label: Object.fromEntries(mocks.map((mock) => [
|
|
646
|
+
mock.label,
|
|
647
|
+
events.filter((event) => event.label === mock.label && event.ok !== false).length
|
|
648
|
+
])),
|
|
649
|
+
failed
|
|
650
|
+
},
|
|
651
|
+
message: failed.length ? `Network mocks failed or were not hit for ${failed.length} mock(s).` : void 0
|
|
652
|
+
};
|
|
653
|
+
}
|
|
577
654
|
function profileStatusFromEvidence(profile, evidence, checks) {
|
|
578
655
|
if (!evidence) return "proof_insufficient";
|
|
579
656
|
const viewports = evidence.viewports || [];
|
|
@@ -588,6 +665,7 @@ function profileStatusFromEvidence(profile, evidence, checks) {
|
|
|
588
665
|
function assessRiddleProofProfileEvidence(profile, evidence, options = {}) {
|
|
589
666
|
const capturedAt = evidence?.captured_at || (/* @__PURE__ */ new Date()).toISOString();
|
|
590
667
|
const checks = evidence ? [
|
|
668
|
+
assessNetworkMocksFromEvidence(profile, evidence),
|
|
591
669
|
assessSetupActionsFromEvidence(profile, evidence),
|
|
592
670
|
...profile.checks.map((check) => assessCheckFromEvidence(check, evidence))
|
|
593
671
|
].filter((check) => Boolean(check)) : [];
|
|
@@ -804,6 +882,49 @@ function horizontalBoundsOverflowPx(value) {
|
|
|
804
882
|
function assessProfile(profile, evidence) {
|
|
805
883
|
const checks = [];
|
|
806
884
|
const viewports = evidence.viewports || [];
|
|
885
|
+
if (profile.target && Array.isArray(profile.target.network_mocks) && profile.target.network_mocks.length) {
|
|
886
|
+
const events = evidence.network_mocks || [];
|
|
887
|
+
const requiredMocks = profile.target.network_mocks.filter((mock) => mock && mock.required !== false);
|
|
888
|
+
const failed = [];
|
|
889
|
+
for (const mock of requiredMocks) {
|
|
890
|
+
const hits = events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
|
|
891
|
+
if (hits < 1) {
|
|
892
|
+
failed.push({
|
|
893
|
+
label: mock.label,
|
|
894
|
+
url: mock.url,
|
|
895
|
+
method: mock.method || null,
|
|
896
|
+
reason: "required_mock_not_hit",
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
for (const event of events) {
|
|
901
|
+
if (event && event.ok === false) {
|
|
902
|
+
failed.push({
|
|
903
|
+
label: event.label || null,
|
|
904
|
+
url: event.url || null,
|
|
905
|
+
method: event.method || null,
|
|
906
|
+
reason: event.reason || event.error || "mock_failed",
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
const hitsByLabel = {};
|
|
911
|
+
for (const mock of profile.target.network_mocks) {
|
|
912
|
+
hitsByLabel[mock.label] = events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
|
|
913
|
+
}
|
|
914
|
+
checks.push({
|
|
915
|
+
type: "network_mocks_succeeded",
|
|
916
|
+
label: "network mocks succeeded",
|
|
917
|
+
status: failed.length ? "failed" : "passed",
|
|
918
|
+
evidence: {
|
|
919
|
+
mock_count: profile.target.network_mocks.length,
|
|
920
|
+
required_count: requiredMocks.length,
|
|
921
|
+
hit_count: events.filter((event) => event && event.ok !== false).length,
|
|
922
|
+
hits_by_label: hitsByLabel,
|
|
923
|
+
failed,
|
|
924
|
+
},
|
|
925
|
+
message: failed.length ? "Network mocks failed or were not hit for " + failed.length + " mock(s)." : undefined,
|
|
926
|
+
});
|
|
927
|
+
}
|
|
807
928
|
if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
|
|
808
929
|
const actionCount = profile.target.setup_actions.length;
|
|
809
930
|
const failed = [];
|
|
@@ -993,6 +1114,7 @@ const profileSlug = ${serializableSlug};
|
|
|
993
1114
|
const capturedAt = new Date().toISOString();
|
|
994
1115
|
const consoleEvents = [];
|
|
995
1116
|
const pageErrors = [];
|
|
1117
|
+
const networkMockEvents = [];
|
|
996
1118
|
page.on("console", (message) => {
|
|
997
1119
|
const type = message.type();
|
|
998
1120
|
if (type === "error" || type === "warning" || type === "assert") {
|
|
@@ -1039,6 +1161,49 @@ function compactSetupResultText(value) {
|
|
|
1039
1161
|
async function setupLocatorVisible(locator, index) {
|
|
1040
1162
|
return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
|
|
1041
1163
|
}
|
|
1164
|
+
async function registerNetworkMocks(mocks) {
|
|
1165
|
+
for (const mock of mocks || []) {
|
|
1166
|
+
await page.route(mock.url, async (route) => {
|
|
1167
|
+
const request = route.request();
|
|
1168
|
+
const method = request.method ? request.method() : "";
|
|
1169
|
+
if (mock.method && method.toUpperCase() !== String(mock.method).toUpperCase()) {
|
|
1170
|
+
await route.continue();
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
try {
|
|
1174
|
+
const headers = { ...(mock.headers || {}) };
|
|
1175
|
+
let body = mock.body || "";
|
|
1176
|
+
let contentType = mock.content_type || headers["content-type"] || headers["Content-Type"] || "text/plain";
|
|
1177
|
+
if (mock.body_json !== undefined) {
|
|
1178
|
+
body = JSON.stringify(mock.body_json);
|
|
1179
|
+
contentType = mock.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
|
|
1180
|
+
}
|
|
1181
|
+
networkMockEvents.push({
|
|
1182
|
+
ok: true,
|
|
1183
|
+
label: mock.label,
|
|
1184
|
+
url: request.url(),
|
|
1185
|
+
method,
|
|
1186
|
+
status: mock.status || 200,
|
|
1187
|
+
});
|
|
1188
|
+
await route.fulfill({
|
|
1189
|
+
status: mock.status || 200,
|
|
1190
|
+
headers,
|
|
1191
|
+
contentType,
|
|
1192
|
+
body,
|
|
1193
|
+
});
|
|
1194
|
+
} catch (error) {
|
|
1195
|
+
networkMockEvents.push({
|
|
1196
|
+
ok: false,
|
|
1197
|
+
label: mock.label,
|
|
1198
|
+
url: request.url(),
|
|
1199
|
+
method,
|
|
1200
|
+
error: String(error && error.message ? error.message : error).slice(0, 1000),
|
|
1201
|
+
});
|
|
1202
|
+
throw error;
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1042
1207
|
async function executeSetupAction(action, ordinal) {
|
|
1043
1208
|
const type = setupActionType(action);
|
|
1044
1209
|
const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null };
|
|
@@ -1300,6 +1465,7 @@ function buildProfileEvidence(currentViewports) {
|
|
|
1300
1465
|
fatal_count: consoleEvents.filter((event) => event.type === "error" || event.type === "assert").length,
|
|
1301
1466
|
},
|
|
1302
1467
|
page_errors: pageErrors,
|
|
1468
|
+
network_mocks: networkMockEvents.slice(),
|
|
1303
1469
|
dom_summary: {
|
|
1304
1470
|
expected_viewport_count: expectedViewportCount,
|
|
1305
1471
|
viewport_count: currentViewports.length,
|
|
@@ -1309,6 +1475,8 @@ function buildProfileEvidence(currentViewports) {
|
|
|
1309
1475
|
overflow_px: currentViewports.map((viewport) => viewport.overflow_px),
|
|
1310
1476
|
bounds_overflow_px: currentViewports.map((viewport) => viewport.bounds_overflow_px),
|
|
1311
1477
|
overflow_offender_counts: currentViewports.map((viewport) => (viewport.overflow_offenders || []).length),
|
|
1478
|
+
network_mock_count: (profile.target.network_mocks || []).length,
|
|
1479
|
+
network_mock_hit_count: networkMockEvents.filter((event) => event.ok !== false).length,
|
|
1312
1480
|
},
|
|
1313
1481
|
};
|
|
1314
1482
|
}
|
|
@@ -1322,6 +1490,9 @@ async function saveProfileArtifacts(currentViewports) {
|
|
|
1322
1490
|
}
|
|
1323
1491
|
return result;
|
|
1324
1492
|
}
|
|
1493
|
+
await registerNetworkMocks(profile.target.network_mocks || []);
|
|
1494
|
+
consoleEvents.length = 0;
|
|
1495
|
+
pageErrors.length = 0;
|
|
1325
1496
|
let result = await saveProfileArtifacts(viewports);
|
|
1326
1497
|
for (const viewport of profile.target.viewports || []) {
|
|
1327
1498
|
viewports.push(await captureViewport(viewport));
|
package/dist/profile.d.cts
CHANGED
|
@@ -29,6 +29,17 @@ interface RiddleProofProfileSetupAction {
|
|
|
29
29
|
after_ms?: number;
|
|
30
30
|
continue_on_failure?: boolean;
|
|
31
31
|
}
|
|
32
|
+
interface RiddleProofProfileNetworkMock {
|
|
33
|
+
label: string;
|
|
34
|
+
url: string;
|
|
35
|
+
method?: string;
|
|
36
|
+
status: number;
|
|
37
|
+
content_type?: string;
|
|
38
|
+
headers?: Record<string, string>;
|
|
39
|
+
body?: string;
|
|
40
|
+
body_json?: JsonValue;
|
|
41
|
+
required?: boolean;
|
|
42
|
+
}
|
|
32
43
|
interface RiddleProofProfileTarget {
|
|
33
44
|
url?: string;
|
|
34
45
|
route?: string;
|
|
@@ -38,6 +49,7 @@ interface RiddleProofProfileTarget {
|
|
|
38
49
|
wait_for_selector?: string;
|
|
39
50
|
wait_ms?: number;
|
|
40
51
|
setup_actions?: RiddleProofProfileSetupAction[];
|
|
52
|
+
network_mocks?: RiddleProofProfileNetworkMock[];
|
|
41
53
|
}
|
|
42
54
|
interface RiddleProofProfileCheck {
|
|
43
55
|
type: RiddleProofProfileCheckType;
|
|
@@ -126,6 +138,7 @@ interface RiddleProofProfileEvidence {
|
|
|
126
138
|
page_errors: Array<{
|
|
127
139
|
message: string;
|
|
128
140
|
}>;
|
|
141
|
+
network_mocks?: Array<Record<string, JsonValue>>;
|
|
129
142
|
dom_summary?: Record<string, JsonValue>;
|
|
130
143
|
}
|
|
131
144
|
interface RiddleProofProfileCheckResult {
|
|
@@ -209,4 +222,4 @@ declare function buildRiddleProofProfileScript(profile: RiddleProofProfile): str
|
|
|
209
222
|
declare function collectRiddleProfileArtifactRefs(input: unknown): RiddleProofProfileArtifactRef[];
|
|
210
223
|
declare function extractRiddleProofProfileResult(input: unknown): RiddleProofProfileResult | undefined;
|
|
211
224
|
|
|
212
|
-
export { type NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, type RiddleProofProfile, type RiddleProofProfileArtifactRef, type RiddleProofProfileBaselinePolicy, type RiddleProofProfileBoundsOffender, type RiddleProofProfileCheck, type RiddleProofProfileCheckResult, type RiddleProofProfileCheckType, type RiddleProofProfileEvidence, type RiddleProofProfileFailureAction, type RiddleProofProfileResult, type RiddleProofProfileRouteEvidence, type RiddleProofProfileRunner, type RiddleProofProfileSetupAction, type RiddleProofProfileSetupActionType, type RiddleProofProfileStatus, type RiddleProofProfileTarget, type RiddleProofProfileViewport, type RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, extractRiddleProofProfileResult, normalizeRiddleProofProfile, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult };
|
|
225
|
+
export { type NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, type RiddleProofProfile, type RiddleProofProfileArtifactRef, type RiddleProofProfileBaselinePolicy, type RiddleProofProfileBoundsOffender, type RiddleProofProfileCheck, type RiddleProofProfileCheckResult, type RiddleProofProfileCheckType, type RiddleProofProfileEvidence, type RiddleProofProfileFailureAction, type RiddleProofProfileNetworkMock, type RiddleProofProfileResult, type RiddleProofProfileRouteEvidence, type RiddleProofProfileRunner, type RiddleProofProfileSetupAction, type RiddleProofProfileSetupActionType, type RiddleProofProfileStatus, type RiddleProofProfileTarget, type RiddleProofProfileViewport, type RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, extractRiddleProofProfileResult, normalizeRiddleProofProfile, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult };
|
package/dist/profile.d.ts
CHANGED
|
@@ -29,6 +29,17 @@ interface RiddleProofProfileSetupAction {
|
|
|
29
29
|
after_ms?: number;
|
|
30
30
|
continue_on_failure?: boolean;
|
|
31
31
|
}
|
|
32
|
+
interface RiddleProofProfileNetworkMock {
|
|
33
|
+
label: string;
|
|
34
|
+
url: string;
|
|
35
|
+
method?: string;
|
|
36
|
+
status: number;
|
|
37
|
+
content_type?: string;
|
|
38
|
+
headers?: Record<string, string>;
|
|
39
|
+
body?: string;
|
|
40
|
+
body_json?: JsonValue;
|
|
41
|
+
required?: boolean;
|
|
42
|
+
}
|
|
32
43
|
interface RiddleProofProfileTarget {
|
|
33
44
|
url?: string;
|
|
34
45
|
route?: string;
|
|
@@ -38,6 +49,7 @@ interface RiddleProofProfileTarget {
|
|
|
38
49
|
wait_for_selector?: string;
|
|
39
50
|
wait_ms?: number;
|
|
40
51
|
setup_actions?: RiddleProofProfileSetupAction[];
|
|
52
|
+
network_mocks?: RiddleProofProfileNetworkMock[];
|
|
41
53
|
}
|
|
42
54
|
interface RiddleProofProfileCheck {
|
|
43
55
|
type: RiddleProofProfileCheckType;
|
|
@@ -126,6 +138,7 @@ interface RiddleProofProfileEvidence {
|
|
|
126
138
|
page_errors: Array<{
|
|
127
139
|
message: string;
|
|
128
140
|
}>;
|
|
141
|
+
network_mocks?: Array<Record<string, JsonValue>>;
|
|
129
142
|
dom_summary?: Record<string, JsonValue>;
|
|
130
143
|
}
|
|
131
144
|
interface RiddleProofProfileCheckResult {
|
|
@@ -209,4 +222,4 @@ declare function buildRiddleProofProfileScript(profile: RiddleProofProfile): str
|
|
|
209
222
|
declare function collectRiddleProfileArtifactRefs(input: unknown): RiddleProofProfileArtifactRef[];
|
|
210
223
|
declare function extractRiddleProofProfileResult(input: unknown): RiddleProofProfileResult | undefined;
|
|
211
224
|
|
|
212
|
-
export { type NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, type RiddleProofProfile, type RiddleProofProfileArtifactRef, type RiddleProofProfileBaselinePolicy, type RiddleProofProfileBoundsOffender, type RiddleProofProfileCheck, type RiddleProofProfileCheckResult, type RiddleProofProfileCheckType, type RiddleProofProfileEvidence, type RiddleProofProfileFailureAction, type RiddleProofProfileResult, type RiddleProofProfileRouteEvidence, type RiddleProofProfileRunner, type RiddleProofProfileSetupAction, type RiddleProofProfileSetupActionType, type RiddleProofProfileStatus, type RiddleProofProfileTarget, type RiddleProofProfileViewport, type RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, extractRiddleProofProfileResult, normalizeRiddleProofProfile, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult };
|
|
225
|
+
export { type NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, type RiddleProofProfile, type RiddleProofProfileArtifactRef, type RiddleProofProfileBaselinePolicy, type RiddleProofProfileBoundsOffender, type RiddleProofProfileCheck, type RiddleProofProfileCheckResult, type RiddleProofProfileCheckType, type RiddleProofProfileEvidence, type RiddleProofProfileFailureAction, type RiddleProofProfileNetworkMock, type RiddleProofProfileResult, type RiddleProofProfileRouteEvidence, type RiddleProofProfileRunner, type RiddleProofProfileSetupAction, type RiddleProofProfileSetupActionType, type RiddleProofProfileStatus, type RiddleProofProfileTarget, type RiddleProofProfileViewport, type RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, extractRiddleProofProfileResult, normalizeRiddleProofProfile, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult };
|
package/dist/profile.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
resolveRiddleProofProfileTimeoutSec,
|
|
20
20
|
slugifyRiddleProofProfileName,
|
|
21
21
|
summarizeRiddleProofProfileResult
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-FUWINYNX.js";
|
|
23
23
|
export {
|
|
24
24
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
25
25
|
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|