

























@@ -1,5 +1,8 @@
1+import fs from "node:fs/promises";
2+import path from "node:path";
13import type { Page } from "playwright-core";
2-import { describe, expect, it, vi } from "vitest";
4+import { afterEach, describe, expect, it, vi } from "vitest";
5+import { DEFAULT_DOWNLOAD_DIR } from "./paths.js";
36import {
47ensurePageState,
58refLocator,
@@ -8,6 +11,16 @@ import {
811} from "./pw-session.js";
912import { BROWSER_REF_MARKER_ATTRIBUTE } from "./pw-session.page-cdp.js";
101314+type MutableDownload = {
15+suggestedFilename: () => string;
16+saveAs: ReturnType<typeof vi.fn>;
17+path?: () => Promise<string>;
18+};
19+20+afterEach(() => {
21+vi.restoreAllMocks();
22+});
23+1124function fakePage(): {
1225page: Page;
1326handlers: Map<string, Array<(...args: unknown[]) => void>>;
@@ -123,6 +136,80 @@ describe("pw-session role refs cache", () => {
123136});
124137125138describe("pw-session ensurePageState", () => {
139+it("stores unmanaged downloads under unique managed paths", async () => {
140+const { page, handlers } = fakePage();
141+const mkdirSpy = vi.spyOn(fs, "mkdir").mockResolvedValue(undefined);
142+ensurePageState(page);
143+144+const saveAsA = vi.fn(async () => {});
145+const saveAsB = vi.fn(async () => {});
146+const downloadA: MutableDownload = {
147+suggestedFilename: () => "report.pdf",
148+saveAs: saveAsA,
149+};
150+const downloadB: MutableDownload = {
151+suggestedFilename: () => "report.pdf",
152+saveAs: saveAsB,
153+};
154+155+handlers.get("download")?.[0]?.(downloadA);
156+handlers.get("download")?.[0]?.(downloadB);
157+158+const managedPathA = await downloadA.path?.();
159+const managedPathB = await downloadB.path?.();
160+161+expect(managedPathA).not.toBe(managedPathB);
162+expect(path.dirname(managedPathA ?? "")).toBe(DEFAULT_DOWNLOAD_DIR);
163+expect(path.dirname(managedPathB ?? "")).toBe(DEFAULT_DOWNLOAD_DIR);
164+expect(path.basename(managedPathA ?? "")).toMatch(/-report\.pdf$/);
165+expect(path.basename(managedPathB ?? "")).toMatch(/-report\.pdf$/);
166+expect(saveAsA).toHaveBeenCalledWith(managedPathA);
167+expect(saveAsB).toHaveBeenCalledWith(managedPathB);
168+expect(mkdirSpy).toHaveBeenCalledWith(DEFAULT_DOWNLOAD_DIR, { recursive: true });
169+});
170+171+it("suppresses unmanaged download save rejections until path is awaited", async () => {
172+const { page, handlers } = fakePage();
173+vi.spyOn(fs, "mkdir").mockResolvedValue(undefined);
174+ensurePageState(page);
175+const unhandled: unknown[] = [];
176+const onUnhandled = (reason: unknown) => unhandled.push(reason);
177+process.on("unhandledRejection", onUnhandled);
178+179+const err = new Error("save failed");
180+const download: MutableDownload = {
181+suggestedFilename: () => "report.pdf",
182+saveAs: vi.fn(async () => {
183+throw err;
184+}),
185+};
186+187+try {
188+handlers.get("download")?.[0]?.(download);
189+await new Promise((resolve) => setImmediate(resolve));
190+191+expect(unhandled).toEqual([]);
192+await expect(download.path?.()).rejects.toThrow("save failed");
193+} finally {
194+process.off("unhandledRejection", onUnhandled);
195+}
196+});
197+198+it("leaves unmanaged download handling to explicit waiters while armed", () => {
199+const { page, handlers } = fakePage();
200+const state = ensurePageState(page);
201+state.downloadWaiterDepth = 1;
202+const download = {
203+suggestedFilename: () => "report.pdf",
204+saveAs: vi.fn(async () => {}),
205+};
206+207+handlers.get("download")?.[0]?.(download);
208+209+expect(download).not.toHaveProperty("path");
210+expect(download.saveAs).not.toHaveBeenCalled();
211+});
212+126213it("tracks page errors and network requests (best-effort)", () => {
127214const { page, handlers } = fakePage();
128215const state = ensurePageState(page);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。