@ralphralphai/playwright 1.9.0 → 2.1.0
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/README.md +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.mts +19 -2
- package/dist/index.mjs +1 -1
- package/dist/reporter.cjs +37 -11
- package/dist/reporter.cjs.map +1 -1
- package/dist/reporter.d.mts +2 -2
- package/dist/reporter.mjs +37 -11
- package/dist/reporter.mjs.map +1 -1
- package/dist/{upload-BQSCs8By.cjs → upload-BJ-OMhZ8.cjs} +281 -12
- package/dist/upload-BJ-OMhZ8.cjs.map +1 -0
- package/dist/{upload-BZs6Vqix.mjs → upload-BhHRgN8t.mjs} +277 -14
- package/dist/upload-BhHRgN8t.mjs.map +1 -0
- package/package.json +3 -3
- package/dist/upload-BQSCs8By.cjs.map +0 -1
- package/dist/upload-BZs6Vqix.mjs.map +0 -1
|
@@ -70,7 +70,7 @@ function snapshot(value) {
|
|
|
70
70
|
}
|
|
71
71
|
//#endregion
|
|
72
72
|
//#region package.json
|
|
73
|
-
var version = "1.
|
|
73
|
+
var version = "2.1.0";
|
|
74
74
|
//#endregion
|
|
75
75
|
//#region src/actions.ts
|
|
76
76
|
const BINDING = "__ralphAction";
|
|
@@ -547,6 +547,230 @@ async function waitForAnimations(page, timeoutMs) {
|
|
|
547
547
|
if (!(error instanceof _playwright_test.errors.TimeoutError)) throw error;
|
|
548
548
|
}
|
|
549
549
|
}
|
|
550
|
+
const MAX_ARIA_DEPTH = Math.floor(128 / 2) - 2;
|
|
551
|
+
const STYLE_PROPERTIES = [
|
|
552
|
+
"font-family",
|
|
553
|
+
"font-size",
|
|
554
|
+
"font-weight",
|
|
555
|
+
"font-style",
|
|
556
|
+
"line-height",
|
|
557
|
+
"letter-spacing",
|
|
558
|
+
"color",
|
|
559
|
+
"background-color",
|
|
560
|
+
"text-align",
|
|
561
|
+
"text-transform"
|
|
562
|
+
];
|
|
563
|
+
const UNINHERITED_PROPERTIES = {
|
|
564
|
+
"padding-top": "0px",
|
|
565
|
+
"padding-right": "0px",
|
|
566
|
+
"padding-bottom": "0px",
|
|
567
|
+
"padding-left": "0px",
|
|
568
|
+
"margin-top": "0px",
|
|
569
|
+
"margin-right": "0px",
|
|
570
|
+
"margin-bottom": "0px",
|
|
571
|
+
"margin-left": "0px",
|
|
572
|
+
"row-gap": "normal",
|
|
573
|
+
"column-gap": "normal",
|
|
574
|
+
"justify-content": "normal",
|
|
575
|
+
"align-items": "normal",
|
|
576
|
+
"flex-direction": "row",
|
|
577
|
+
"grid-template-columns": "none",
|
|
578
|
+
order: "0",
|
|
579
|
+
"border-width": "0px",
|
|
580
|
+
"border-color": "currentcolor",
|
|
581
|
+
"border-radius": "0px",
|
|
582
|
+
"box-shadow": "none",
|
|
583
|
+
opacity: "1",
|
|
584
|
+
"text-decoration-line": "none"
|
|
585
|
+
};
|
|
586
|
+
const IMAGE_PROPERTIES = {
|
|
587
|
+
width: "auto",
|
|
588
|
+
height: "auto"
|
|
589
|
+
};
|
|
590
|
+
const TRANSPARENT = "rgba(0, 0, 0, 0)";
|
|
591
|
+
const ELEMENT_LOOKUP_TIMEOUT_MS = 2e3;
|
|
592
|
+
/**
|
|
593
|
+
* The page's aria tree as JSON, with each element's box in document
|
|
594
|
+
* coordinates, its distinctive styles, and a hash of its image. Undefined when
|
|
595
|
+
* the snapshot is past the server's limits.
|
|
596
|
+
*
|
|
597
|
+
* The page is not scrolled first, because scrolling changes the page the
|
|
598
|
+
* screenshot just showed: sticky headers, lazy loading, scroll-triggered
|
|
599
|
+
* animation.
|
|
600
|
+
*/
|
|
601
|
+
async function captureAriaSnapshot(page, { viewport, scroll }) {
|
|
602
|
+
const snapshot = await page.locator("body").ariaSnapshotJSON({
|
|
603
|
+
mode: "ai",
|
|
604
|
+
boxes: true,
|
|
605
|
+
depth: MAX_ARIA_DEPTH
|
|
606
|
+
});
|
|
607
|
+
const visible = (await Promise.all(snapshot.map((node) => toDocument(page, node, scroll)))).flatMap((node) => {
|
|
608
|
+
const kept = keepShown(node, viewport.width);
|
|
609
|
+
return kept === void 0 ? [] : [kept];
|
|
610
|
+
});
|
|
611
|
+
const described = await Promise.all(visible.map((node) => describe(page, node, {})));
|
|
612
|
+
const json = Buffer.from(JSON.stringify(fillMissingRefs(described)));
|
|
613
|
+
return json.byteLength > 4194304 ? void 0 : json;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Moves the viewport boxes Playwright reports into the full-page screenshot's
|
|
617
|
+
* space. This has to happen before `keepShown`, which in viewport space would
|
|
618
|
+
* drop everything scrolled above the viewport, and no later move brings those
|
|
619
|
+
* back.
|
|
620
|
+
*
|
|
621
|
+
* An iframe's descendants are boxed in its own viewport, so they move by
|
|
622
|
+
* where that viewport sits: the iframe's box plus its border and padding.
|
|
623
|
+
*/
|
|
624
|
+
async function toDocument(page, node, offset) {
|
|
625
|
+
const box = node.box && {
|
|
626
|
+
...node.box,
|
|
627
|
+
x: node.box.x + offset.x,
|
|
628
|
+
y: node.box.y + offset.y
|
|
629
|
+
};
|
|
630
|
+
let childOffset = offset;
|
|
631
|
+
if (node.role === "iframe" && box !== void 0) {
|
|
632
|
+
const inset = (node.ref === void 0 ? void 0 : await readContentInset(page, node.ref)) ?? {
|
|
633
|
+
x: 0,
|
|
634
|
+
y: 0
|
|
635
|
+
};
|
|
636
|
+
childOffset = {
|
|
637
|
+
x: box.x + inset.x,
|
|
638
|
+
y: box.y + inset.y
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
return {
|
|
642
|
+
...node,
|
|
643
|
+
...box && { box },
|
|
644
|
+
...node.children && { children: await Promise.all(node.children.map((child) => typeof child === "string" ? child : toDocument(page, child, childOffset))) }
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
async function readContentInset(page, ref) {
|
|
648
|
+
try {
|
|
649
|
+
return await page.locator(`aria-ref=${ref}`).evaluate((element) => {
|
|
650
|
+
const computed = getComputedStyle(element);
|
|
651
|
+
return {
|
|
652
|
+
x: element.clientLeft + parseFloat(computed.paddingLeft),
|
|
653
|
+
y: element.clientTop + parseFloat(computed.paddingTop)
|
|
654
|
+
};
|
|
655
|
+
}, void 0, { timeout: ELEMENT_LOOKUP_TIMEOUT_MS });
|
|
656
|
+
} catch {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* In document space, the only boxes no scroll reveals are those past the left
|
|
662
|
+
* or right of the viewport's width (a collapsed sidebar, an off-canvas menu)
|
|
663
|
+
* or above the top of the document.
|
|
664
|
+
*/
|
|
665
|
+
function isReachable(box, viewportWidth) {
|
|
666
|
+
if (box === void 0) return false;
|
|
667
|
+
return box.x < viewportWidth && box.x + box.width > 0 && box.y + box.height > 0;
|
|
668
|
+
}
|
|
669
|
+
function isShown(node, viewportWidth) {
|
|
670
|
+
return node.ref !== void 0 && isReachable(node.box, viewportWidth);
|
|
671
|
+
}
|
|
672
|
+
function keepShown(node, viewportWidth) {
|
|
673
|
+
const shown = isShown(node, viewportWidth);
|
|
674
|
+
const children = (node.children ?? []).flatMap((child) => {
|
|
675
|
+
if (typeof child === "string") return shown ? [child] : [];
|
|
676
|
+
const kept = keepShown(child, viewportWidth);
|
|
677
|
+
return kept === void 0 ? [] : [kept];
|
|
678
|
+
});
|
|
679
|
+
if (!shown && children.length === 0) return;
|
|
680
|
+
return node.children === void 0 ? node : {
|
|
681
|
+
...node,
|
|
682
|
+
children
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
function fillMissingRefs(nodes) {
|
|
686
|
+
let generated = 0;
|
|
687
|
+
const fill = (child) => {
|
|
688
|
+
if (typeof child === "string") return child;
|
|
689
|
+
generated += 1;
|
|
690
|
+
const ref = child.ref ?? `n${generated}`;
|
|
691
|
+
return child.children === void 0 ? {
|
|
692
|
+
...child,
|
|
693
|
+
ref
|
|
694
|
+
} : {
|
|
695
|
+
...child,
|
|
696
|
+
ref,
|
|
697
|
+
children: child.children.map(fill)
|
|
698
|
+
};
|
|
699
|
+
};
|
|
700
|
+
return nodes.map(fill);
|
|
701
|
+
}
|
|
702
|
+
async function readElement(page, ref, isImage) {
|
|
703
|
+
try {
|
|
704
|
+
return await page.locator(`aria-ref=${ref}`).evaluate((element, { properties, isImage }) => {
|
|
705
|
+
const computed = getComputedStyle(element);
|
|
706
|
+
const style = Object.fromEntries(properties.map((property) => [property, computed.getPropertyValue(property)]));
|
|
707
|
+
if (!isImage) return { style };
|
|
708
|
+
if (element instanceof HTMLImageElement) return {
|
|
709
|
+
style,
|
|
710
|
+
image: { src: element.currentSrc || element.src }
|
|
711
|
+
};
|
|
712
|
+
if (element instanceof SVGElement) return {
|
|
713
|
+
style,
|
|
714
|
+
image: { markup: element.outerHTML }
|
|
715
|
+
};
|
|
716
|
+
const backgroundUrl = /url\("(.+)"\)/.exec(computed.backgroundImage)?.[1];
|
|
717
|
+
return backgroundUrl === void 0 ? { style } : {
|
|
718
|
+
style,
|
|
719
|
+
image: { src: new URL(backgroundUrl, document.baseURI).href }
|
|
720
|
+
};
|
|
721
|
+
}, {
|
|
722
|
+
properties: [
|
|
723
|
+
...STYLE_PROPERTIES,
|
|
724
|
+
...Object.keys(UNINHERITED_PROPERTIES),
|
|
725
|
+
...isImage ? Object.keys(IMAGE_PROPERTIES) : []
|
|
726
|
+
],
|
|
727
|
+
isImage
|
|
728
|
+
}, { timeout: ELEMENT_LOOKUP_TIMEOUT_MS });
|
|
729
|
+
} catch {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
function sha256(data) {
|
|
734
|
+
return (0, node_crypto.createHash)("sha256").update(data).digest("hex");
|
|
735
|
+
}
|
|
736
|
+
async function hashImage(page, image) {
|
|
737
|
+
if ("markup" in image) return sha256(image.markup);
|
|
738
|
+
if (image.src === "") return;
|
|
739
|
+
if (image.src.startsWith("data:")) return sha256(image.src);
|
|
740
|
+
try {
|
|
741
|
+
const response = await page.request.get(image.src);
|
|
742
|
+
try {
|
|
743
|
+
return response.ok() ? sha256(await response.body()) : void 0;
|
|
744
|
+
} finally {
|
|
745
|
+
await response.dispose();
|
|
746
|
+
}
|
|
747
|
+
} catch {
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
function distinctStyle(style, inherited) {
|
|
752
|
+
const distinct = Object.entries(style).filter(([property, value]) => {
|
|
753
|
+
if (property === "border-color") return style["border-width"] !== "0px";
|
|
754
|
+
const initial = UNINHERITED_PROPERTIES[property] ?? IMAGE_PROPERTIES[property];
|
|
755
|
+
if (initial !== void 0) return value !== initial;
|
|
756
|
+
if (property === "background-color" && value === TRANSPARENT) return false;
|
|
757
|
+
return inherited[property] !== value;
|
|
758
|
+
});
|
|
759
|
+
return distinct.length === 0 ? void 0 : Object.fromEntries(distinct);
|
|
760
|
+
}
|
|
761
|
+
async function describe(page, child, inherited) {
|
|
762
|
+
if (typeof child === "string") return child;
|
|
763
|
+
const facts = child.ref === void 0 ? void 0 : await readElement(page, child.ref, child.role === "img");
|
|
764
|
+
const style = facts === void 0 ? void 0 : distinctStyle(facts.style, inherited);
|
|
765
|
+
const imageHash = facts?.image === void 0 ? void 0 : await hashImage(page, facts.image);
|
|
766
|
+
const children = child.children === void 0 ? void 0 : await Promise.all(child.children.map((grandchild) => describe(page, grandchild, facts?.style ?? inherited)));
|
|
767
|
+
return {
|
|
768
|
+
...child,
|
|
769
|
+
...style && { style },
|
|
770
|
+
...imageHash && { imageHash },
|
|
771
|
+
...children && { children }
|
|
772
|
+
};
|
|
773
|
+
}
|
|
550
774
|
//#endregion
|
|
551
775
|
//#region src/conditions.ts
|
|
552
776
|
const declared = (declaration = {}) => {
|
|
@@ -726,6 +950,8 @@ const MANIFEST_ATTACHMENT = "ralph-raw-graph";
|
|
|
726
950
|
const REPORTER_ENV = "RALPH_PLAYWRIGHT_REPORTER";
|
|
727
951
|
/** Prefix of the attachment holding each recorded node's screenshot; the node id follows. */
|
|
728
952
|
const SCREENSHOT_ATTACHMENT_PREFIX = "ralph-screenshot-";
|
|
953
|
+
/** Prefix of the attachment holding each recorded node's aria snapshot; the node id follows. */
|
|
954
|
+
const ARIA_SNAPSHOT_ATTACHMENT_PREFIX = "ralph-aria-snapshot-";
|
|
729
955
|
/** Once per worker: a run without the tracker says so once, not per node. */
|
|
730
956
|
let warnedNoTracker = false;
|
|
731
957
|
const DEFAULT_SETTLE_MS = 150;
|
|
@@ -943,11 +1169,14 @@ var Recorder = class {
|
|
|
943
1169
|
const deadline = startDeadline(this.recordTimeoutMs, "Ralph recording timed out.");
|
|
944
1170
|
const { wait } = deadline;
|
|
945
1171
|
const navigatedAway = () => state.generation !== generation || page.url() !== request.actualUrl;
|
|
946
|
-
const
|
|
947
|
-
deadline.signal.throwIfAborted();
|
|
1172
|
+
const checkPage = () => {
|
|
948
1173
|
if (navigatedAway()) throw new Error("The page navigated before its recording completed.");
|
|
949
1174
|
if (page.isClosed()) throw new Error("The page closed before its recording completed.");
|
|
950
1175
|
};
|
|
1176
|
+
const check = () => {
|
|
1177
|
+
deadline.signal.throwIfAborted();
|
|
1178
|
+
checkPage();
|
|
1179
|
+
};
|
|
951
1180
|
try {
|
|
952
1181
|
check();
|
|
953
1182
|
if (request.trigger !== "explicit") await this.waitForPageToSettle(page, deadline, check);
|
|
@@ -973,6 +1202,8 @@ var Recorder = class {
|
|
|
973
1202
|
check();
|
|
974
1203
|
const after = await wait(readLayout(page));
|
|
975
1204
|
check();
|
|
1205
|
+
const snapshot = await Promise.race([captureAriaSnapshot(page, after).catch(() => void 0), (0, node_timers_promises.setTimeout)(Math.max(0, deadline.endsAt - Date.now()), void 0, { ref: false })]);
|
|
1206
|
+
checkPage();
|
|
976
1207
|
const recorded = {
|
|
977
1208
|
...request,
|
|
978
1209
|
...withConditions(conditions),
|
|
@@ -982,7 +1213,8 @@ var Recorder = class {
|
|
|
982
1213
|
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
983
1214
|
layout: before,
|
|
984
1215
|
geometryStable: JSON.stringify(before) === JSON.stringify(after),
|
|
985
|
-
screenshot: await this.saveScreenshot(request.nodeId, image)
|
|
1216
|
+
screenshot: await this.saveScreenshot(request.nodeId, image),
|
|
1217
|
+
...snapshot && { ariaSnapshot: await this.saveAriaSnapshot(request.nodeId, snapshot) }
|
|
986
1218
|
};
|
|
987
1219
|
const retaken = sameNode ? this.nodes.findIndex(({ nodeId }) => nodeId === state.lastRecorded?.nodeId) : -1;
|
|
988
1220
|
if (retaken === -1) {
|
|
@@ -1042,6 +1274,21 @@ var Recorder = class {
|
|
|
1042
1274
|
contentType: image.contentType
|
|
1043
1275
|
};
|
|
1044
1276
|
}
|
|
1277
|
+
/** Writes the snapshot to the test output and attaches it to the report. */
|
|
1278
|
+
async saveAriaSnapshot(nodeId, snapshot) {
|
|
1279
|
+
const path = "ralph/" + nodeId + ".aria.json";
|
|
1280
|
+
const attachmentName = ARIA_SNAPSHOT_ATTACHMENT_PREFIX + nodeId;
|
|
1281
|
+
await (0, node_fs_promises.mkdir)(this.testInfo.outputPath(OUTPUT_DIR), { recursive: true });
|
|
1282
|
+
await (0, node_fs_promises.writeFile)(this.testInfo.outputPath(path), snapshot);
|
|
1283
|
+
await this.testInfo.attach(attachmentName, {
|
|
1284
|
+
...this.testInfo.config.preserveOutput === "always" ? { path: this.testInfo.outputPath(path) } : { body: snapshot },
|
|
1285
|
+
contentType: "application/json"
|
|
1286
|
+
});
|
|
1287
|
+
return {
|
|
1288
|
+
path,
|
|
1289
|
+
attachmentName
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1045
1292
|
/** Gives each node the actions taken since the node it was reached from was requested. */
|
|
1046
1293
|
attachActions(nodes) {
|
|
1047
1294
|
const byId = new Map(nodes.map((item) => [item.nodeId, item]));
|
|
@@ -1176,11 +1423,17 @@ function resolveUploadOptions(options) {
|
|
|
1176
1423
|
}
|
|
1177
1424
|
const RAW_GRAPH_BUNDLE_INDEX = "raw_graph.json";
|
|
1178
1425
|
const RAW_GRAPH_BUNDLE_IMAGE_DIR = "assets/images";
|
|
1426
|
+
const RAW_GRAPH_BUNDLE_SNAPSHOT_DIR = "assets/snapshots";
|
|
1179
1427
|
/** Where a node's screenshot sits inside the bundle. */
|
|
1180
1428
|
function bundleImagePath(nodeId, contentType) {
|
|
1181
1429
|
if (!/^[A-Za-z0-9-]{1,64}$/.test(nodeId)) throw new Error(`Ralph node id is not path-safe: ${nodeId}`);
|
|
1182
1430
|
return `${RAW_GRAPH_BUNDLE_IMAGE_DIR}/${nodeId}${imageExtension(contentType)}`;
|
|
1183
1431
|
}
|
|
1432
|
+
/** Where a node's aria snapshot sits inside the bundle. */
|
|
1433
|
+
function bundleSnapshotPath(nodeId) {
|
|
1434
|
+
if (!/^[A-Za-z0-9-]{1,64}$/.test(nodeId)) throw new Error(`Ralph node id is not path-safe: ${nodeId}`);
|
|
1435
|
+
return `${RAW_GRAPH_BUNDLE_SNAPSHOT_DIR}/${nodeId}.json`;
|
|
1436
|
+
}
|
|
1184
1437
|
const TOO_BIG = "This run is too big to upload to Ralph. Record fewer pages or screen sizes, or split the tests across runs.";
|
|
1185
1438
|
var TooBigError = class extends Error {
|
|
1186
1439
|
constructor() {
|
|
@@ -1191,23 +1444,33 @@ var TooBigError = class extends Error {
|
|
|
1191
1444
|
* Packs the manifest and screenshots into a gzipped tar archive, produced as it
|
|
1192
1445
|
* is read so the compressed bundle is never held whole:
|
|
1193
1446
|
*
|
|
1194
|
-
* raw_graph.json
|
|
1447
|
+
* raw_graph.json { manifest, screenshots: [{ nodeId, path }], snapshots: [...] }
|
|
1195
1448
|
* assets/images/<id>.webp raw screenshot bytes (.png when too large for WebP)
|
|
1449
|
+
* assets/snapshots/<id>.json the node's aria snapshot, when it has one
|
|
1196
1450
|
*
|
|
1197
1451
|
* Fails with a `TooBigError` once the compressed bytes pass the limit.
|
|
1198
1452
|
*/
|
|
1199
|
-
function rawGraphBundleStream(manifest, screenshots) {
|
|
1453
|
+
function rawGraphBundleStream(manifest, screenshots, snapshots) {
|
|
1200
1454
|
if (Buffer.byteLength(JSON.stringify(manifest)) > 16777216 || screenshots.length > 5e3 || screenshots.reduce((total, screenshot) => total + screenshot.bytes.byteLength, 0) > 268435456) throw new TooBigError();
|
|
1201
1455
|
const images = screenshots.map(({ nodeId, contentType, bytes }) => ({
|
|
1202
1456
|
nodeId,
|
|
1203
1457
|
path: bundleImagePath(nodeId, contentType),
|
|
1204
1458
|
bytes
|
|
1205
1459
|
}));
|
|
1460
|
+
const files = [...images, ...snapshots.map(({ nodeId, bytes }) => ({
|
|
1461
|
+
nodeId,
|
|
1462
|
+
path: bundleSnapshotPath(nodeId),
|
|
1463
|
+
bytes
|
|
1464
|
+
}))];
|
|
1206
1465
|
const index = {
|
|
1207
1466
|
manifest,
|
|
1208
1467
|
screenshots: images.map(({ nodeId, path }) => ({
|
|
1209
1468
|
nodeId,
|
|
1210
1469
|
path
|
|
1470
|
+
})),
|
|
1471
|
+
snapshots: snapshots.map(({ nodeId }) => ({
|
|
1472
|
+
nodeId,
|
|
1473
|
+
path: bundleSnapshotPath(nodeId)
|
|
1211
1474
|
}))
|
|
1212
1475
|
};
|
|
1213
1476
|
const attrs = {
|
|
@@ -1221,7 +1484,7 @@ function rawGraphBundleStream(manifest, screenshots) {
|
|
|
1221
1484
|
name: RAW_GRAPH_BUNDLE_INDEX,
|
|
1222
1485
|
...attrs
|
|
1223
1486
|
}, JSON.stringify(index)).on("error", onEntryError);
|
|
1224
|
-
for (const { path, bytes } of
|
|
1487
|
+
for (const { path, bytes } of files) tar.entry({
|
|
1225
1488
|
name: path,
|
|
1226
1489
|
...attrs
|
|
1227
1490
|
}, Buffer.from(bytes)).on("error", onEntryError);
|
|
@@ -1238,9 +1501,9 @@ function rawGraphBundleStream(manifest, screenshots) {
|
|
|
1238
1501
|
})());
|
|
1239
1502
|
}
|
|
1240
1503
|
/** The bundle `uploadRawGraph` streams, whole, for saving or inspecting it. */
|
|
1241
|
-
async function createRawGraphBundle(manifest, screenshots) {
|
|
1504
|
+
async function createRawGraphBundle(manifest, screenshots, snapshots = []) {
|
|
1242
1505
|
const chunks = [];
|
|
1243
|
-
for await (const chunk of rawGraphBundleStream(manifest, screenshots)) chunks.push(chunk);
|
|
1506
|
+
for await (const chunk of rawGraphBundleStream(manifest, screenshots, snapshots)) chunks.push(chunk);
|
|
1244
1507
|
return Buffer.concat(chunks);
|
|
1245
1508
|
}
|
|
1246
1509
|
function isLoopback(url) {
|
|
@@ -1315,7 +1578,7 @@ async function reserve(options, route, body) {
|
|
|
1315
1578
|
* `prepareArtifactUpload` has to have reserved. Ralph processes it after this
|
|
1316
1579
|
* returns; the result page shows when it is ready.
|
|
1317
1580
|
*/
|
|
1318
|
-
async function uploadRawGraph(manifest, screenshots, options) {
|
|
1581
|
+
async function uploadRawGraph(manifest, screenshots, options, snapshots = []) {
|
|
1319
1582
|
const url = checkedApiUrl(options, `/api/upload/web/apps/${options.appId}/artifact/${encodeURIComponent(manifest.artifactId)}`);
|
|
1320
1583
|
const storage = process.env["RALPH_UPLOAD_STORAGE"] || "gcs";
|
|
1321
1584
|
if (storage !== "gcs" && storage !== "local") throw new Error(`RALPH_UPLOAD_STORAGE must be gcs or local, not ${storage}.`);
|
|
@@ -1324,7 +1587,7 @@ async function uploadRawGraph(manifest, screenshots, options) {
|
|
|
1324
1587
|
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) throw new Error("uploadTimeoutMs in the Ralph reporter options must be a positive number of milliseconds.");
|
|
1325
1588
|
const receipt = await request(url, {
|
|
1326
1589
|
headers: { "content-type": "application/gzip" },
|
|
1327
|
-
body: node_stream.Readable.toWeb(rawGraphBundleStream(manifest, screenshots)),
|
|
1590
|
+
body: node_stream.Readable.toWeb(rawGraphBundleStream(manifest, screenshots, snapshots)),
|
|
1328
1591
|
duplex: "half"
|
|
1329
1592
|
}, options, timeoutMs);
|
|
1330
1593
|
if (receipt["result"] !== "ok" || receipt["status"] !== "queued" || receipt["artifactId"] !== manifest.artifactId || receipt["resultUrl"] !== void 0 && typeof receipt["resultUrl"] !== "string" || typeof receipt["replayed"] !== "boolean") throw unreadableReply(url);
|
|
@@ -1404,6 +1667,12 @@ Object.defineProperty(exports, "bundleImagePath", {
|
|
|
1404
1667
|
return bundleImagePath;
|
|
1405
1668
|
}
|
|
1406
1669
|
});
|
|
1670
|
+
Object.defineProperty(exports, "bundleSnapshotPath", {
|
|
1671
|
+
enumerable: true,
|
|
1672
|
+
get: function() {
|
|
1673
|
+
return bundleSnapshotPath;
|
|
1674
|
+
}
|
|
1675
|
+
});
|
|
1407
1676
|
Object.defineProperty(exports, "createRawGraphBundle", {
|
|
1408
1677
|
enumerable: true,
|
|
1409
1678
|
get: function() {
|
|
@@ -1465,4 +1734,4 @@ Object.defineProperty(exports, "version", {
|
|
|
1465
1734
|
}
|
|
1466
1735
|
});
|
|
1467
1736
|
|
|
1468
|
-
//# sourceMappingURL=upload-
|
|
1737
|
+
//# sourceMappingURL=upload-BJ-OMhZ8.cjs.map
|