@ralphralphai/playwright 2.5.0 → 2.7.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/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/reporter.cjs +1 -1
- package/dist/reporter.mjs +1 -1
- package/dist/{upload-jup_jLlm.mjs → upload-BsCzkoup.mjs} +163 -17
- package/dist/upload-BsCzkoup.mjs.map +1 -0
- package/dist/{upload-D40MqSQa.cjs → upload-_UWss__c.cjs} +163 -17
- package/dist/upload-_UWss__c.cjs.map +1 -0
- package/package.json +1 -1
- package/dist/upload-D40MqSQa.cjs.map +0 -1
- package/dist/upload-jup_jLlm.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_upload = require("./upload-
|
|
2
|
+
const require_upload = require("./upload-_UWss__c.cjs");
|
|
3
3
|
let node_path = require("node:path");
|
|
4
4
|
node_path = require_upload.__toESM(node_path, 1);
|
|
5
5
|
let _playwright_test = require("@playwright/test");
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as resolveMode, a as prepareArtifactUpload, f as Recorder, g as loadConfigSync, h as loadConfig, i as createRawGraphBundle, l as uploadRawGraph, p as visitAllLinks, u as MANIFEST_ATTACHMENT } from "./upload-
|
|
1
|
+
import { _ as resolveMode, a as prepareArtifactUpload, f as Recorder, g as loadConfigSync, h as loadConfig, i as createRawGraphBundle, l as uploadRawGraph, p as visitAllLinks, u as MANIFEST_ATTACHMENT } from "./upload-BsCzkoup.mjs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { expect, mergeTests, test as test$1 } from "@playwright/test";
|
|
4
4
|
//#region src/screens.ts
|
package/dist/reporter.cjs
CHANGED
package/dist/reporter.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as resolveMode, a as prepareArtifactUpload, c as resultLink, d as REPORTER_ENV, l as uploadRawGraph, m as version, n as bundleImagePath, o as registerDeployment, r as bundleSnapshotPath, s as resolveUploadOptions, t as RAW_GRAPH_BUNDLE_INDEX, u as MANIFEST_ATTACHMENT } from "./upload-
|
|
1
|
+
import { _ as resolveMode, a as prepareArtifactUpload, c as resultLink, d as REPORTER_ENV, l as uploadRawGraph, m as version, n as bundleImagePath, o as registerDeployment, r as bundleSnapshotPath, s as resolveUploadOptions, t as RAW_GRAPH_BUNDLE_INDEX, u as MANIFEST_ATTACHMENT } from "./upload-BsCzkoup.mjs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -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.7.0";
|
|
49
49
|
//#endregion
|
|
50
50
|
//#region src/actions.ts
|
|
51
51
|
const BINDING = "__ralphAction";
|
|
@@ -526,30 +526,57 @@ async function waitForAnimations(page, timeoutMs) {
|
|
|
526
526
|
//#region src/image_responses.ts
|
|
527
527
|
const IMAGE_HASH_TIMEOUT_MS = 3e4;
|
|
528
528
|
const hashesByPage = /* @__PURE__ */ new WeakMap();
|
|
529
|
+
/**
|
|
530
|
+
* The latest hash per url across all of a context's pages. The HTTP cache is
|
|
531
|
+
* shared by the context, so this is the copy a 304 tells any of them to reuse.
|
|
532
|
+
*/
|
|
533
|
+
const hashesByContext = /* @__PURE__ */ new WeakMap();
|
|
529
534
|
function sha256(data) {
|
|
530
535
|
return createHash("sha256").update(data).digest("hex");
|
|
531
536
|
}
|
|
532
537
|
/**
|
|
533
|
-
* Hashes every image the
|
|
534
|
-
* bytes the page showed instead of fetching them a second time. A
|
|
535
|
-
* of a page's images comes as a burst, which hosts such as
|
|
538
|
+
* Hashes every image the context's pages load, as they load, so a snapshot
|
|
539
|
+
* hashes the bytes the page showed instead of fetching them a second time. A
|
|
540
|
+
* second fetch of a page's images comes as a burst, which hosts such as
|
|
536
541
|
* upload.wikimedia.org answer with 429 for a share of them, and the image then
|
|
537
542
|
* reads as replaced. Only the hash is kept, not the body. Entries persist
|
|
538
543
|
* across navigations until another request for the same image replaces them
|
|
539
544
|
* or the page closes.
|
|
545
|
+
*
|
|
546
|
+
* Watched on the context rather than each page: Playwright reports a popup
|
|
547
|
+
* only once its first navigation has committed, and the images that document
|
|
548
|
+
* requests early are gone by the time a listener can be added to the page.
|
|
540
549
|
*/
|
|
541
|
-
function watchImageResponses(
|
|
542
|
-
if (
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
if (request.resourceType() !== "image") return;
|
|
550
|
+
function watchImageResponses(context) {
|
|
551
|
+
if (hashesByContext.has(context)) return;
|
|
552
|
+
const latest = /* @__PURE__ */ new Map();
|
|
553
|
+
hashesByContext.set(context, latest);
|
|
554
|
+
context.on("request", (request) => {
|
|
555
|
+
if (request.resourceType() !== "image" || request.serviceWorker()) return;
|
|
556
|
+
const hashes = pageHashes(request.frame().page());
|
|
557
|
+
const cached = latest.get(request.url());
|
|
547
558
|
const hash = request.response().then(async (response) => {
|
|
559
|
+
if (response?.status() === 304) return cached;
|
|
548
560
|
return response?.ok() ? sha256(await response.body()) : void 0;
|
|
549
561
|
}).catch(() => void 0);
|
|
550
|
-
for (
|
|
562
|
+
for (const url of redirectChain(request)) {
|
|
563
|
+
hashes.set(url, hash);
|
|
564
|
+
latest.set(url, hash);
|
|
565
|
+
}
|
|
551
566
|
});
|
|
567
|
+
}
|
|
568
|
+
function pageHashes(page) {
|
|
569
|
+
const existing = hashesByPage.get(page);
|
|
570
|
+
if (existing) return existing;
|
|
571
|
+
const hashes = /* @__PURE__ */ new Map();
|
|
572
|
+
hashesByPage.set(page, hashes);
|
|
552
573
|
page.once("close", () => hashesByPage.delete(page));
|
|
574
|
+
return hashes;
|
|
575
|
+
}
|
|
576
|
+
function redirectChain(request) {
|
|
577
|
+
const urls = [];
|
|
578
|
+
for (let redirected = request; redirected !== null; redirected = redirected.redirectedFrom()) urls.push(redirected.url());
|
|
579
|
+
return urls;
|
|
553
580
|
}
|
|
554
581
|
/**
|
|
555
582
|
* The hash from the latest observed request for this URL, across navigations.
|
|
@@ -629,8 +656,10 @@ async function captureAriaSnapshot(page, { viewport, scroll }) {
|
|
|
629
656
|
boxes: true,
|
|
630
657
|
depth: MAX_ARIA_DEPTH
|
|
631
658
|
});
|
|
632
|
-
const
|
|
633
|
-
|
|
659
|
+
const placed = await Promise.all(snapshot.map((node) => toDocument(page, node, scroll)));
|
|
660
|
+
const clips = await readClips(page, placed);
|
|
661
|
+
const visible = placed.flatMap((node) => {
|
|
662
|
+
const kept = keepShown(node, viewport.width, clips, void 0);
|
|
634
663
|
return kept === void 0 ? [] : [kept];
|
|
635
664
|
});
|
|
636
665
|
const described = await Promise.all(visible.map((node) => describe(page, node, {}, false)));
|
|
@@ -694,12 +723,129 @@ function isReachable(box, viewportWidth) {
|
|
|
694
723
|
function isShown(node, viewportWidth) {
|
|
695
724
|
return node.ref !== void 0 && isReachable(node.box, viewportWidth);
|
|
696
725
|
}
|
|
697
|
-
function
|
|
726
|
+
function edgesOf(box) {
|
|
727
|
+
return {
|
|
728
|
+
left: box.x,
|
|
729
|
+
top: box.y,
|
|
730
|
+
right: box.x + box.width,
|
|
731
|
+
bottom: box.y + box.height
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
function intersect(a, b) {
|
|
735
|
+
if (a === void 0) return b;
|
|
736
|
+
return {
|
|
737
|
+
left: Math.max(a.left, b.left),
|
|
738
|
+
top: Math.max(a.top, b.top),
|
|
739
|
+
right: Math.min(a.right, b.right),
|
|
740
|
+
bottom: Math.min(a.bottom, b.bottom)
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
const ROUNDING_SLACK = 1;
|
|
744
|
+
function sticksOut(inner, outer) {
|
|
745
|
+
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;
|
|
746
|
+
}
|
|
747
|
+
function overlaps(a, b) {
|
|
748
|
+
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;
|
|
749
|
+
}
|
|
750
|
+
function isOutside(box, clip) {
|
|
751
|
+
return box.x >= clip.right || box.x + box.width <= clip.left || box.y >= clip.bottom || box.y + box.height <= clip.top;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* The area each element is clipped to by its ancestors' `overflow`, in
|
|
755
|
+
* document space, by ref.
|
|
756
|
+
*
|
|
757
|
+
* The boxes are not clipped (the rows of a 448px scroll box run thousands of
|
|
758
|
+
* pixels down the page), and reading an element's ancestors is a page round
|
|
759
|
+
* trip. Only an element that may overflow one is read: a root, one sticking
|
|
760
|
+
* out of its parent, or one running into its next sibling. The clipping
|
|
761
|
+
* ancestor itself is often missing from the tree, since Playwright unwraps a
|
|
762
|
+
* nameless wrapper with one child. Every other element takes its parent's
|
|
763
|
+
* clip.
|
|
764
|
+
*/
|
|
765
|
+
async function readClips(page, nodes) {
|
|
766
|
+
const candidates = [];
|
|
767
|
+
const visit = (node, parent, next, frame) => {
|
|
768
|
+
if (node.box !== void 0 && (parent === void 0 || sticksOut(node.box, parent) || next?.box !== void 0 && overlaps(node.box, next.box))) candidates.push({
|
|
769
|
+
node,
|
|
770
|
+
frame
|
|
771
|
+
});
|
|
772
|
+
const childFrame = node.role === "iframe" && node.box !== void 0 ? intersect(frame, edgesOf(node.box)) : frame;
|
|
773
|
+
const elements = (node.children ?? []).filter((child) => {
|
|
774
|
+
return typeof child !== "string";
|
|
775
|
+
});
|
|
776
|
+
elements.forEach((child, index) => {
|
|
777
|
+
visit(child, node.box, elements[index + 1], childFrame);
|
|
778
|
+
});
|
|
779
|
+
};
|
|
780
|
+
nodes.forEach((node, index) => {
|
|
781
|
+
visit(node, void 0, nodes[index + 1], void 0);
|
|
782
|
+
});
|
|
783
|
+
const clips = await Promise.all(candidates.map(async ({ node, frame }) => {
|
|
784
|
+
const clip = await readClip(page, node);
|
|
785
|
+
return clip === void 0 || node.ref === void 0 ? [] : [[node.ref, intersect(frame, clip)]];
|
|
786
|
+
}));
|
|
787
|
+
return new Map(clips.flat());
|
|
788
|
+
}
|
|
789
|
+
async function readClip(page, node) {
|
|
790
|
+
if (node.box === void 0 || node.ref === void 0) return;
|
|
791
|
+
let read;
|
|
792
|
+
try {
|
|
793
|
+
read = await page.locator(`aria-ref=${node.ref}`).evaluate((element) => {
|
|
794
|
+
const clip = {
|
|
795
|
+
left: -Infinity,
|
|
796
|
+
top: -Infinity,
|
|
797
|
+
right: Infinity,
|
|
798
|
+
bottom: Infinity
|
|
799
|
+
};
|
|
800
|
+
let position = getComputedStyle(element).position;
|
|
801
|
+
for (let ancestor = element.parentElement; ancestor !== null && ancestor !== document.body && ancestor !== document.documentElement; ancestor = ancestor.parentElement) {
|
|
802
|
+
if (position === "fixed") break;
|
|
803
|
+
const style = getComputedStyle(ancestor);
|
|
804
|
+
if (position === "absolute" && style.position === "static" && style.transform === "none") continue;
|
|
805
|
+
position = style.position;
|
|
806
|
+
const clipsX = style.overflowX !== "visible";
|
|
807
|
+
const clipsY = style.overflowY !== "visible";
|
|
808
|
+
if (!clipsX && !clipsY || style.display === "inline") continue;
|
|
809
|
+
const rect = ancestor.getBoundingClientRect();
|
|
810
|
+
const left = rect.left + ancestor.clientLeft;
|
|
811
|
+
const top = rect.top + ancestor.clientTop;
|
|
812
|
+
if (clipsX) {
|
|
813
|
+
clip.left = Math.max(clip.left, left);
|
|
814
|
+
clip.right = Math.min(clip.right, left + ancestor.clientWidth);
|
|
815
|
+
}
|
|
816
|
+
if (clipsY) {
|
|
817
|
+
clip.top = Math.max(clip.top, top);
|
|
818
|
+
clip.bottom = Math.min(clip.bottom, top + ancestor.clientHeight);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
const own = element.getBoundingClientRect();
|
|
822
|
+
return {
|
|
823
|
+
x: own.x,
|
|
824
|
+
y: own.y,
|
|
825
|
+
clip
|
|
826
|
+
};
|
|
827
|
+
}, void 0, { timeout: ELEMENT_LOOKUP_TIMEOUT_MS });
|
|
828
|
+
} catch {
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
const dx = node.box.x - read.x;
|
|
832
|
+
const dy = node.box.y - read.y;
|
|
833
|
+
return {
|
|
834
|
+
left: read.clip.left + dx,
|
|
835
|
+
top: read.clip.top + dy,
|
|
836
|
+
right: read.clip.right + dx,
|
|
837
|
+
bottom: read.clip.bottom + dy
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
function keepShown(node, viewportWidth, clips, inherited) {
|
|
698
841
|
if (node.role === "iframe" && node.box !== void 0 && (node.box.width < MIN_IFRAME_SIZE || node.box.height < MIN_IFRAME_SIZE)) return;
|
|
842
|
+
const clip = (node.ref === void 0 ? void 0 : clips.get(node.ref)) ?? inherited;
|
|
843
|
+
if (clip !== void 0 && node.box !== void 0 && isOutside(node.box, clip)) return;
|
|
699
844
|
const shown = isShown(node, viewportWidth);
|
|
845
|
+
const childClip = node.role === "iframe" && node.box !== void 0 ? intersect(clip, edgesOf(node.box)) : clip;
|
|
700
846
|
const children = (node.children ?? []).flatMap((child) => {
|
|
701
847
|
if (typeof child === "string") return shown ? [child] : [];
|
|
702
|
-
const kept = keepShown(child, viewportWidth);
|
|
848
|
+
const kept = keepShown(child, viewportWidth, clips, childClip);
|
|
703
849
|
return kept === void 0 ? [] : [kept];
|
|
704
850
|
});
|
|
705
851
|
if (!shown && children.length === 0) return;
|
|
@@ -1280,6 +1426,7 @@ var Recorder = class {
|
|
|
1280
1426
|
freezer
|
|
1281
1427
|
});
|
|
1282
1428
|
this.actions.observe(context);
|
|
1429
|
+
watchImageResponses(context);
|
|
1283
1430
|
context.on("page", onPage);
|
|
1284
1431
|
for (const page of context.pages()) onPage(page);
|
|
1285
1432
|
return ready;
|
|
@@ -1380,7 +1527,6 @@ var Recorder = class {
|
|
|
1380
1527
|
}
|
|
1381
1528
|
}).catch(() => void 0);
|
|
1382
1529
|
page.on("framenavigated", state.onNavigation);
|
|
1383
|
-
watchImageResponses(page);
|
|
1384
1530
|
this.urlChanged(state);
|
|
1385
1531
|
}
|
|
1386
1532
|
urlChanged(state) {
|
|
@@ -1907,4 +2053,4 @@ async function responseDetail(response) {
|
|
|
1907
2053
|
//#endregion
|
|
1908
2054
|
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 };
|
|
1909
2055
|
|
|
1910
|
-
//# sourceMappingURL=upload-
|
|
2056
|
+
//# sourceMappingURL=upload-BsCzkoup.mjs.map
|