@ralphralphai/playwright 2.4.0 → 2.6.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 +44 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +9 -1
- package/dist/index.mjs +3 -3
- package/dist/index.mjs.map +1 -1
- package/dist/reporter.cjs +1 -1
- package/dist/reporter.mjs +1 -1
- package/dist/{upload-CUtC_rnN.mjs → upload-CGIMWRQM.mjs} +338 -23
- package/dist/upload-CGIMWRQM.mjs.map +1 -0
- package/dist/{upload-FIxKqKb3.cjs → upload-H-EycbCA.cjs} +338 -23
- package/dist/upload-H-EycbCA.cjs.map +1 -0
- package/package.json +1 -1
- package/dist/upload-CUtC_rnN.mjs.map +0 -1
- package/dist/upload-FIxKqKb3.cjs.map +0 -1
|
@@ -45,7 +45,7 @@ function snapshot(value) {
|
|
|
45
45
|
}
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region package.json
|
|
48
|
-
var version = "2.
|
|
48
|
+
var version = "2.6.0";
|
|
49
49
|
//#endregion
|
|
50
50
|
//#region src/actions.ts
|
|
51
51
|
const BINDING = "__ralphAction";
|
|
@@ -629,8 +629,10 @@ async function captureAriaSnapshot(page, { viewport, scroll }) {
|
|
|
629
629
|
boxes: true,
|
|
630
630
|
depth: MAX_ARIA_DEPTH
|
|
631
631
|
});
|
|
632
|
-
const
|
|
633
|
-
|
|
632
|
+
const placed = await Promise.all(snapshot.map((node) => toDocument(page, node, scroll)));
|
|
633
|
+
const clips = await readClips(page, placed);
|
|
634
|
+
const visible = placed.flatMap((node) => {
|
|
635
|
+
const kept = keepShown(node, viewport.width, clips, void 0);
|
|
634
636
|
return kept === void 0 ? [] : [kept];
|
|
635
637
|
});
|
|
636
638
|
const described = await Promise.all(visible.map((node) => describe(page, node, {}, false)));
|
|
@@ -694,15 +696,134 @@ function isReachable(box, viewportWidth) {
|
|
|
694
696
|
function isShown(node, viewportWidth) {
|
|
695
697
|
return node.ref !== void 0 && isReachable(node.box, viewportWidth);
|
|
696
698
|
}
|
|
697
|
-
function
|
|
699
|
+
function edgesOf(box) {
|
|
700
|
+
return {
|
|
701
|
+
left: box.x,
|
|
702
|
+
top: box.y,
|
|
703
|
+
right: box.x + box.width,
|
|
704
|
+
bottom: box.y + box.height
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
function intersect(a, b) {
|
|
708
|
+
if (a === void 0) return b;
|
|
709
|
+
return {
|
|
710
|
+
left: Math.max(a.left, b.left),
|
|
711
|
+
top: Math.max(a.top, b.top),
|
|
712
|
+
right: Math.min(a.right, b.right),
|
|
713
|
+
bottom: Math.min(a.bottom, b.bottom)
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
const ROUNDING_SLACK = 1;
|
|
717
|
+
function sticksOut(inner, outer) {
|
|
718
|
+
return inner.x < outer.x - ROUNDING_SLACK || inner.y < outer.y - ROUNDING_SLACK || inner.x + inner.width > outer.x + outer.width + ROUNDING_SLACK || inner.y + inner.height > outer.y + outer.height + ROUNDING_SLACK;
|
|
719
|
+
}
|
|
720
|
+
function overlaps(a, b) {
|
|
721
|
+
return Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x) > ROUNDING_SLACK && Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y) > ROUNDING_SLACK;
|
|
722
|
+
}
|
|
723
|
+
function isOutside(box, clip) {
|
|
724
|
+
return box.x >= clip.right || box.x + box.width <= clip.left || box.y >= clip.bottom || box.y + box.height <= clip.top;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* The area each element is clipped to by its ancestors' `overflow`, in
|
|
728
|
+
* document space, by ref.
|
|
729
|
+
*
|
|
730
|
+
* The boxes are not clipped (the rows of a 448px scroll box run thousands of
|
|
731
|
+
* pixels down the page), and reading an element's ancestors is a page round
|
|
732
|
+
* trip. Only an element that may overflow one is read: a root, one sticking
|
|
733
|
+
* out of its parent, or one running into its next sibling. The clipping
|
|
734
|
+
* ancestor itself is often missing from the tree, since Playwright unwraps a
|
|
735
|
+
* nameless wrapper with one child. Every other element takes its parent's
|
|
736
|
+
* clip.
|
|
737
|
+
*/
|
|
738
|
+
async function readClips(page, nodes) {
|
|
739
|
+
const candidates = [];
|
|
740
|
+
const visit = (node, parent, next, frame) => {
|
|
741
|
+
if (node.box !== void 0 && (parent === void 0 || sticksOut(node.box, parent) || next?.box !== void 0 && overlaps(node.box, next.box))) candidates.push({
|
|
742
|
+
node,
|
|
743
|
+
frame
|
|
744
|
+
});
|
|
745
|
+
const childFrame = node.role === "iframe" && node.box !== void 0 ? intersect(frame, edgesOf(node.box)) : frame;
|
|
746
|
+
const elements = (node.children ?? []).filter((child) => {
|
|
747
|
+
return typeof child !== "string";
|
|
748
|
+
});
|
|
749
|
+
elements.forEach((child, index) => {
|
|
750
|
+
visit(child, node.box, elements[index + 1], childFrame);
|
|
751
|
+
});
|
|
752
|
+
};
|
|
753
|
+
nodes.forEach((node, index) => {
|
|
754
|
+
visit(node, void 0, nodes[index + 1], void 0);
|
|
755
|
+
});
|
|
756
|
+
const clips = await Promise.all(candidates.map(async ({ node, frame }) => {
|
|
757
|
+
const clip = await readClip(page, node);
|
|
758
|
+
return clip === void 0 || node.ref === void 0 ? [] : [[node.ref, intersect(frame, clip)]];
|
|
759
|
+
}));
|
|
760
|
+
return new Map(clips.flat());
|
|
761
|
+
}
|
|
762
|
+
async function readClip(page, node) {
|
|
763
|
+
if (node.box === void 0 || node.ref === void 0) return;
|
|
764
|
+
let read;
|
|
765
|
+
try {
|
|
766
|
+
read = await page.locator(`aria-ref=${node.ref}`).evaluate((element) => {
|
|
767
|
+
const clip = {
|
|
768
|
+
left: -Infinity,
|
|
769
|
+
top: -Infinity,
|
|
770
|
+
right: Infinity,
|
|
771
|
+
bottom: Infinity
|
|
772
|
+
};
|
|
773
|
+
let position = getComputedStyle(element).position;
|
|
774
|
+
for (let ancestor = element.parentElement; ancestor !== null && ancestor !== document.body && ancestor !== document.documentElement; ancestor = ancestor.parentElement) {
|
|
775
|
+
if (position === "fixed") break;
|
|
776
|
+
const style = getComputedStyle(ancestor);
|
|
777
|
+
if (position === "absolute" && style.position === "static" && style.transform === "none") continue;
|
|
778
|
+
position = style.position;
|
|
779
|
+
const clipsX = style.overflowX !== "visible";
|
|
780
|
+
const clipsY = style.overflowY !== "visible";
|
|
781
|
+
if (!clipsX && !clipsY || style.display === "inline") continue;
|
|
782
|
+
const rect = ancestor.getBoundingClientRect();
|
|
783
|
+
const left = rect.left + ancestor.clientLeft;
|
|
784
|
+
const top = rect.top + ancestor.clientTop;
|
|
785
|
+
if (clipsX) {
|
|
786
|
+
clip.left = Math.max(clip.left, left);
|
|
787
|
+
clip.right = Math.min(clip.right, left + ancestor.clientWidth);
|
|
788
|
+
}
|
|
789
|
+
if (clipsY) {
|
|
790
|
+
clip.top = Math.max(clip.top, top);
|
|
791
|
+
clip.bottom = Math.min(clip.bottom, top + ancestor.clientHeight);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
const own = element.getBoundingClientRect();
|
|
795
|
+
return {
|
|
796
|
+
x: own.x,
|
|
797
|
+
y: own.y,
|
|
798
|
+
clip
|
|
799
|
+
};
|
|
800
|
+
}, void 0, { timeout: ELEMENT_LOOKUP_TIMEOUT_MS });
|
|
801
|
+
} catch {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
const dx = node.box.x - read.x;
|
|
805
|
+
const dy = node.box.y - read.y;
|
|
806
|
+
return {
|
|
807
|
+
left: read.clip.left + dx,
|
|
808
|
+
top: read.clip.top + dy,
|
|
809
|
+
right: read.clip.right + dx,
|
|
810
|
+
bottom: read.clip.bottom + dy
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
function keepShown(node, viewportWidth, clips, inherited) {
|
|
698
814
|
if (node.role === "iframe" && node.box !== void 0 && (node.box.width < MIN_IFRAME_SIZE || node.box.height < MIN_IFRAME_SIZE)) return;
|
|
815
|
+
const clip = (node.ref === void 0 ? void 0 : clips.get(node.ref)) ?? inherited;
|
|
816
|
+
if (clip !== void 0 && node.box !== void 0 && isOutside(node.box, clip)) return;
|
|
699
817
|
const shown = isShown(node, viewportWidth);
|
|
818
|
+
const childClip = node.role === "iframe" && node.box !== void 0 ? intersect(clip, edgesOf(node.box)) : clip;
|
|
700
819
|
const children = (node.children ?? []).flatMap((child) => {
|
|
701
820
|
if (typeof child === "string") return shown ? [child] : [];
|
|
702
|
-
const kept = keepShown(child, viewportWidth);
|
|
821
|
+
const kept = keepShown(child, viewportWidth, clips, childClip);
|
|
703
822
|
return kept === void 0 ? [] : [kept];
|
|
704
823
|
});
|
|
705
824
|
if (!shown && children.length === 0) return;
|
|
825
|
+
const [only] = children;
|
|
826
|
+
if (node.role === "generic" && !node.name && (node.children?.length ?? 0) > 1 && children.length === 1 && typeof only !== "string" && only?.ref !== void 0) return only;
|
|
706
827
|
return node.children === void 0 ? node : {
|
|
707
828
|
...node,
|
|
708
829
|
children
|
|
@@ -800,6 +921,179 @@ async function describe(page, child, inherited, insideDynamic) {
|
|
|
800
921
|
};
|
|
801
922
|
}
|
|
802
923
|
//#endregion
|
|
924
|
+
//#region src/animation_observer.ts
|
|
925
|
+
/** Runs in the page. Keeps native animations and DOM changes observable without app hooks. */
|
|
926
|
+
function observeAnimations() {
|
|
927
|
+
const paused = /* @__PURE__ */ new Set();
|
|
928
|
+
const roots = /* @__PURE__ */ new Set();
|
|
929
|
+
let dirty = true;
|
|
930
|
+
const observer = new MutationObserver(() => {
|
|
931
|
+
dirty = true;
|
|
932
|
+
});
|
|
933
|
+
const collect = (root, all) => {
|
|
934
|
+
if (!roots.has(root)) {
|
|
935
|
+
roots.add(root);
|
|
936
|
+
observer.observe(root, {
|
|
937
|
+
subtree: true,
|
|
938
|
+
childList: true,
|
|
939
|
+
attributes: true,
|
|
940
|
+
characterData: true
|
|
941
|
+
});
|
|
942
|
+
dirty = true;
|
|
943
|
+
}
|
|
944
|
+
root.getAnimations().forEach((animation) => all.add(animation));
|
|
945
|
+
for (const element of root.querySelectorAll("*")) if (element.shadowRoot) collect(element.shadowRoot, all);
|
|
946
|
+
};
|
|
947
|
+
return {
|
|
948
|
+
sample(finishFinite) {
|
|
949
|
+
const all = /* @__PURE__ */ new Set();
|
|
950
|
+
collect(document, all);
|
|
951
|
+
let changed = dirty;
|
|
952
|
+
dirty = false;
|
|
953
|
+
for (const animation of all) {
|
|
954
|
+
if (animation.playState !== "running") continue;
|
|
955
|
+
if (finishFinite && animation.playbackRate !== 0 && Number.isFinite(animation.effect?.getComputedTiming().endTime)) animation.finish();
|
|
956
|
+
else {
|
|
957
|
+
animation.pause();
|
|
958
|
+
paused.add(animation);
|
|
959
|
+
}
|
|
960
|
+
changed = true;
|
|
961
|
+
}
|
|
962
|
+
return changed;
|
|
963
|
+
},
|
|
964
|
+
async ready() {
|
|
965
|
+
await Promise.all([...paused].map((animation) => animation.ready));
|
|
966
|
+
},
|
|
967
|
+
resume() {
|
|
968
|
+
observer.disconnect();
|
|
969
|
+
for (const animation of paused) if (animation.playState === "paused") animation.play();
|
|
970
|
+
}
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
//#endregion
|
|
974
|
+
//#region src/capture_freezer.ts
|
|
975
|
+
const PAUSE_LEAD_MS = 100;
|
|
976
|
+
const ANIMATION_QUIET_MS = 150;
|
|
977
|
+
const MAX_ANIMATION_SETTLE_MS = 1500;
|
|
978
|
+
/** One owner of the context-wide clock; captures in different tabs queue here. */
|
|
979
|
+
var CaptureFreezer = class {
|
|
980
|
+
context;
|
|
981
|
+
tail = Promise.resolve();
|
|
982
|
+
closed = false;
|
|
983
|
+
constructor(context) {
|
|
984
|
+
this.context = context;
|
|
985
|
+
context.once("close", () => {
|
|
986
|
+
this.closed = true;
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
install() {
|
|
990
|
+
if (this.context.pages().some((page) => page.url() !== "about:blank")) throw new Error("Ralph freezeDuringCapture requires the clock before page scripts run. Await ralph.observe(context) before creating or navigating pages in a custom context.");
|
|
991
|
+
return this.context.clock.install();
|
|
992
|
+
}
|
|
993
|
+
async capture(page, signal, capture, deadline = Date.now() + 5e3) {
|
|
994
|
+
const task = this.tail.then(async () => {
|
|
995
|
+
signal.throwIfAborted();
|
|
996
|
+
const animations = /* @__PURE__ */ new Map();
|
|
997
|
+
try {
|
|
998
|
+
await this.settleAnimations(page, animations, signal, deadline);
|
|
999
|
+
await this.pauseClock(page, signal);
|
|
1000
|
+
signal.throwIfAborted();
|
|
1001
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
1002
|
+
await this.sampleAnimations(page, animations, signal, false);
|
|
1003
|
+
await this.waitForAnimationsReady(animations, signal);
|
|
1004
|
+
}
|
|
1005
|
+
return await capture();
|
|
1006
|
+
} finally {
|
|
1007
|
+
try {
|
|
1008
|
+
await this.resumeAnimations(animations);
|
|
1009
|
+
} finally {
|
|
1010
|
+
if (!this.closed) await this.context.clock.resume();
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
this.tail = task.catch(() => void 0);
|
|
1015
|
+
return task;
|
|
1016
|
+
}
|
|
1017
|
+
async settleAnimations(page, animations, signal, deadline) {
|
|
1018
|
+
const now = Date.now();
|
|
1019
|
+
const endsAt = now + Math.min(MAX_ANIMATION_SETTLE_MS, Math.max(0, (deadline - now) / 2));
|
|
1020
|
+
let quietSince = now;
|
|
1021
|
+
while (Date.now() < endsAt) {
|
|
1022
|
+
if (await this.sampleAnimations(page, animations, signal, true)) quietSince = Date.now();
|
|
1023
|
+
if (Date.now() - quietSince >= ANIMATION_QUIET_MS) return;
|
|
1024
|
+
await setTimeout$1(Math.max(0, Math.min(50, endsAt - Date.now())), void 0, { signal }).catch((error) => {
|
|
1025
|
+
signal.throwIfAborted();
|
|
1026
|
+
throw error;
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
async sampleAnimations(page, animations, signal, finishFinite) {
|
|
1031
|
+
let changed = false;
|
|
1032
|
+
for (const frame of page.frames()) {
|
|
1033
|
+
signal.throwIfAborted();
|
|
1034
|
+
try {
|
|
1035
|
+
let handle = animations.get(frame);
|
|
1036
|
+
if (!handle) {
|
|
1037
|
+
handle = await frame.evaluateHandle(observeAnimations);
|
|
1038
|
+
animations.set(frame, handle);
|
|
1039
|
+
}
|
|
1040
|
+
changed = await handle.evaluate((state, finish) => state.sample(finish), finishFinite) || changed;
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
if (!frame.isDetached()) throw error;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
signal.throwIfAborted();
|
|
1046
|
+
return changed;
|
|
1047
|
+
}
|
|
1048
|
+
async waitForAnimationsReady(animations, signal) {
|
|
1049
|
+
signal.throwIfAborted();
|
|
1050
|
+
let onAbort;
|
|
1051
|
+
const aborted = new Promise((_, reject) => {
|
|
1052
|
+
onAbort = () => reject(signal.reason);
|
|
1053
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1054
|
+
});
|
|
1055
|
+
try {
|
|
1056
|
+
await Promise.race([Promise.all([...animations].map(async ([frame, handle]) => {
|
|
1057
|
+
if (frame.isDetached()) return;
|
|
1058
|
+
try {
|
|
1059
|
+
await handle.evaluate((state) => state.ready());
|
|
1060
|
+
} catch (error) {
|
|
1061
|
+
if (!frame.isDetached()) throw error;
|
|
1062
|
+
}
|
|
1063
|
+
})), aborted]);
|
|
1064
|
+
} finally {
|
|
1065
|
+
signal.removeEventListener("abort", onAbort);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
async resumeAnimations(animations) {
|
|
1069
|
+
const restored = await Promise.allSettled([...animations.values()].map(async (handle) => {
|
|
1070
|
+
try {
|
|
1071
|
+
await handle.evaluate((state) => state.resume());
|
|
1072
|
+
} catch (error) {
|
|
1073
|
+
if (!destroyedContext(error)) throw error;
|
|
1074
|
+
} finally {
|
|
1075
|
+
await handle.dispose();
|
|
1076
|
+
}
|
|
1077
|
+
}));
|
|
1078
|
+
for (const result of restored) if (result.status === "rejected") throw result.reason;
|
|
1079
|
+
}
|
|
1080
|
+
async pauseClock(page, signal) {
|
|
1081
|
+
for (let attempt = 0;; attempt++) {
|
|
1082
|
+
const time = await page.evaluate(() => Date.now());
|
|
1083
|
+
signal.throwIfAborted();
|
|
1084
|
+
try {
|
|
1085
|
+
await this.context.clock.pauseAt(time + PAUSE_LEAD_MS);
|
|
1086
|
+
return;
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
if (attempt > 0 || !(error instanceof Error) || !error.message.includes("Cannot fast-forward to the past")) throw error;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
function destroyedContext(error) {
|
|
1094
|
+
return error instanceof Error && /Execution context was destroyed|Cannot find context|Target page, context or browser has been closed|Frame was detached/.test(error.message);
|
|
1095
|
+
}
|
|
1096
|
+
//#endregion
|
|
803
1097
|
//#region src/conditions.ts
|
|
804
1098
|
const declared = (declaration = {}) => {
|
|
805
1099
|
const conditions = {};
|
|
@@ -861,11 +1155,11 @@ function nodeKey(conditions) {
|
|
|
861
1155
|
*/
|
|
862
1156
|
const WEBP_QUALITY = 80;
|
|
863
1157
|
const WEBP_MAX_DIMENSION = 16383;
|
|
864
|
-
async function takeScreenshot(page, deadline) {
|
|
1158
|
+
async function takeScreenshot(page, deadline, frozen = false) {
|
|
865
1159
|
const options = {
|
|
866
1160
|
fullPage: true,
|
|
867
1161
|
scale: "css",
|
|
868
|
-
animations: "disabled"
|
|
1162
|
+
animations: frozen ? "allow" : "disabled"
|
|
869
1163
|
};
|
|
870
1164
|
const webp = await page.screenshot({
|
|
871
1165
|
...options,
|
|
@@ -1094,22 +1388,30 @@ var Recorder = class {
|
|
|
1094
1388
|
/** Starts recording URL changes in every current and future tab of `context`. */
|
|
1095
1389
|
observe(context) {
|
|
1096
1390
|
if (this.closed) throw new Error("The Ralph recorder has finished.");
|
|
1097
|
-
|
|
1391
|
+
const existing = this.contexts.get(context);
|
|
1392
|
+
if (existing) return existing.ready;
|
|
1393
|
+
const freezer = this.options.freezeDuringCapture ? new CaptureFreezer(context) : void 0;
|
|
1394
|
+
const ready = freezer?.install() ?? Promise.resolve();
|
|
1098
1395
|
const onPage = (page) => this.watchPage(page);
|
|
1099
|
-
this.contexts.set(context,
|
|
1396
|
+
this.contexts.set(context, {
|
|
1397
|
+
onPage,
|
|
1398
|
+
ready,
|
|
1399
|
+
freezer
|
|
1400
|
+
});
|
|
1100
1401
|
this.actions.observe(context);
|
|
1101
1402
|
context.on("page", onPage);
|
|
1102
1403
|
for (const page of context.pages()) onPage(page);
|
|
1404
|
+
return ready;
|
|
1103
1405
|
}
|
|
1104
1406
|
async recordNode(page, options) {
|
|
1105
|
-
this.observe(page.context());
|
|
1407
|
+
await this.observe(page.context());
|
|
1106
1408
|
const state = this.pages.get(page);
|
|
1107
1409
|
if (!state) throw new Error("Cannot record a closed browser page.");
|
|
1108
1410
|
if (this.paused) return;
|
|
1109
1411
|
await this.enqueueCreateNodeRequest(state, "explicit", options);
|
|
1110
1412
|
}
|
|
1111
1413
|
async setNodeConditions(page, declaration) {
|
|
1112
|
-
this.observe(page.context());
|
|
1414
|
+
await this.observe(page.context());
|
|
1113
1415
|
const state = this.pages.get(page);
|
|
1114
1416
|
if (!state) throw new Error("Cannot tag a closed browser page.");
|
|
1115
1417
|
const key = Symbol();
|
|
@@ -1140,8 +1442,11 @@ var Recorder = class {
|
|
|
1140
1442
|
}
|
|
1141
1443
|
/** Stops watching `context` and waits for its queued recordings. */
|
|
1142
1444
|
async stopContext(context) {
|
|
1143
|
-
const
|
|
1144
|
-
if (
|
|
1445
|
+
const observed = this.contexts.get(context);
|
|
1446
|
+
if (observed) {
|
|
1447
|
+
context.off("page", observed.onPage);
|
|
1448
|
+
await observed.ready;
|
|
1449
|
+
}
|
|
1145
1450
|
const states = [...this.pages.values()].filter((state) => state.page.context() === context);
|
|
1146
1451
|
for (const state of states) state.page.off("framenavigated", state.onNavigation);
|
|
1147
1452
|
await Promise.all(states.map((state) => state.tail));
|
|
@@ -1257,6 +1562,7 @@ var Recorder = class {
|
|
|
1257
1562
|
};
|
|
1258
1563
|
try {
|
|
1259
1564
|
check();
|
|
1565
|
+
await wait(this.contexts.get(page.context()).ready);
|
|
1260
1566
|
if (request.trigger !== "explicit") await this.waitForPageToSettle(page, deadline, check);
|
|
1261
1567
|
check();
|
|
1262
1568
|
const conditions = this.declaredConditions(state);
|
|
@@ -1269,18 +1575,27 @@ var Recorder = class {
|
|
|
1269
1575
|
warnedNoTracker = true;
|
|
1270
1576
|
console.warn("Ralph: this page runs no tracker, so the node conditions the test declares cannot be checked against the tags the app reports. Production events must carry the same tags, or these nodes will match no visitors.");
|
|
1271
1577
|
}
|
|
1272
|
-
|
|
1578
|
+
const freezer = this.contexts.get(page.context()).freezer;
|
|
1579
|
+
if (this.options.reduceMotion && !freezer) {
|
|
1273
1580
|
check();
|
|
1274
1581
|
await wait(waitForAnimations(page, (deadline.endsAt - Date.now()) / 2));
|
|
1275
1582
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1583
|
+
const capture = async () => {
|
|
1584
|
+
check();
|
|
1585
|
+
const before = await wait(readLayout(page));
|
|
1586
|
+
check();
|
|
1587
|
+
const image = await wait(takeScreenshot(page, deadline.endsAt, !!freezer));
|
|
1588
|
+
check();
|
|
1589
|
+
const after = await wait(readLayout(page));
|
|
1590
|
+
check();
|
|
1591
|
+
return {
|
|
1592
|
+
before,
|
|
1593
|
+
image,
|
|
1594
|
+
after,
|
|
1595
|
+
snapshot: await Promise.race([captureAriaSnapshot(page, after).catch(() => void 0), setTimeout$1(Math.max(0, deadline.endsAt - Date.now()), void 0, { ref: false })])
|
|
1596
|
+
};
|
|
1597
|
+
};
|
|
1598
|
+
const { before, image, after, snapshot } = freezer ? await freezer.capture(page, deadline.signal, capture, deadline.endsAt) : await capture();
|
|
1284
1599
|
checkPage();
|
|
1285
1600
|
const recorded = {
|
|
1286
1601
|
...request,
|
|
@@ -1711,4 +2026,4 @@ async function responseDetail(response) {
|
|
|
1711
2026
|
//#endregion
|
|
1712
2027
|
export { resolveMode as _, prepareArtifactUpload as a, resultLink as c, REPORTER_ENV as d, Recorder as f, loadConfigSync as g, loadConfig as h, createRawGraphBundle as i, uploadRawGraph as l, version as m, bundleImagePath as n, registerDeployment as o, visitAllLinks as p, bundleSnapshotPath as r, resolveUploadOptions as s, RAW_GRAPH_BUNDLE_INDEX as t, MANIFEST_ATTACHMENT as u };
|
|
1713
2028
|
|
|
1714
|
-
//# sourceMappingURL=upload-
|
|
2029
|
+
//# sourceMappingURL=upload-CGIMWRQM.mjs.map
|