"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const test_1 = require("@playwright/test");
const fs_1 = require("fs");
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const v3_js_1 = require("../../lib/v3/v3.js");
const v3_config_js_1 = require("./v3.config.js");
const frame_js_1 = require("../../lib/v3/understudy/frame.js");
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
test_1.test.describe("Page.screenshot options", () => {
    let v3;
    test_1.test.beforeEach(async () => {
        v3 = new v3_js_1.V3(v3_config_js_1.v3TestConfig);
        await v3.init();
    });
    test_1.test.afterEach(async () => {
        await v3?.close?.().catch(() => { });
    });
    (0, test_1.test)("rejects clip combined with fullPage", async () => {
        const page = v3.context.pages()[0];
        await page.goto("data:text/html,<html><body>test</body></html>");
        await (0, test_1.expect)(page.screenshot({
            fullPage: true,
            clip: { x: 0, y: 0, width: 100, height: 100 },
        })).rejects.toThrow(/clip and fullPage/);
    });
    (0, test_1.test)("rejects unsupported image type", async () => {
        const page = v3.context.pages()[0];
        await page.goto("data:text/html,<html><body>noop</body></html>");
        await (0, test_1.expect)(page.screenshot({
            // @ts-expect-error intentional invalid type for runtime validation
            type: "webp",
        })).rejects.toThrow(/unsupported image type/);
    });
    (0, test_1.test)("rejects jpeg quality for png screenshots", async () => {
        const page = v3.context.pages()[0];
        await page.goto("data:text/html,<html><body>noop</body></html>");
        await (0, test_1.expect)(page.screenshot({ type: "png", quality: 50 })).rejects.toThrow(/quality option is only valid/);
    });
    (0, test_1.test)("honours timeout option", async () => {
        const page = v3.context.pages()[0];
        await page.goto("data:text/html,<html><body>noop</body></html>");
        const mainFrame = page.mainFrame();
        const originalScreenshot = mainFrame.screenshot.bind(mainFrame);
        mainFrame.screenshot = async () => {
            await wait(50);
            return Buffer.from("late");
        };
        try {
            await (0, test_1.expect)(page.screenshot({ timeout: 10 })).rejects.toThrow(/timeout/);
        }
        finally {
            mainFrame.screenshot = originalScreenshot;
        }
    });
    (0, test_1.test)("applies advanced options and cleans up overlays", async () => {
        const page = v3.context.pages()[0];
        const screenshotTimeout = process.env.CI ? 15000 : 5000;
        const testStart = Date.now();
        console.log(`[screenshot-test] start ${new Date(testStart).toISOString()} timeout=${screenshotTimeout}`);
        const html = `
      <!doctype html>
      <html>
        <head>
          <meta charset="utf-8" />
          <style>
            body { background: #aaccee; margin: 0; height: 100vh; display: flex; flex-direction: column; align-items: flex-start; }
            .mask-target { width: 80px; height: 80px; margin: 40px; background: rgb(0, 180, 60); animation: pulse 1s infinite alternate; }
            @keyframes pulse { from { transform: scale(1); } to { transform: scale(1.2); } }
          </style>
        </head>
        <body>
          <div class="mask-target"></div>
          <div class="mask-target"></div>
          <input id="focus-me" value="focus" />
          <script>document.getElementById('focus-me').focus();</script>
        </body>
      </html>
    `;
        await page.goto("data:text/html," + encodeURIComponent(html));
        console.log(`[screenshot-test] page loaded in ${Date.now() - testStart}ms`);
        const maskLocator = page.locator(".mask-target");
        const tempPath = path.join(os.tmpdir(), `stagehand-screenshot-${Date.now()}-${Math.random().toString(36).slice(2)}.jpeg`);
        console.log(`[screenshot-test] tempPath=${tempPath}`);
        const targetId = page.targetId();
        const screenshotCalls = [];
        const evaluateCalls = [];
        const originalScreenshot = frame_js_1.Frame.prototype.screenshot;
        const originalEvaluate = frame_js_1.Frame.prototype.evaluate;
        // Hook Frame.screenshot so we can assert which options reach CDP without writing real data.
        frame_js_1.Frame.prototype.screenshot = async function screenshotSpy(options) {
            const frame = this;
            if (frame.pageId === targetId) {
                screenshotCalls.push({ frameId: frame.frameId, options });
                return Buffer.from("stub-image");
            }
            return originalScreenshot.call(this, options);
        };
        // Spy on Frame.evaluate to capture the arguments used to inject CSS/masks.
        frame_js_1.Frame.prototype.evaluate = async function evaluateSpy(expression, arg) {
            const frame = this;
            if (frame.pageId === targetId) {
                evaluateCalls.push({ frameId: frame.frameId, arg });
            }
            return originalEvaluate.call(this, expression, arg);
        };
        const internalPage = page;
        const sendCalls = [];
        const originalSend = internalPage.mainSession.send.bind(internalPage.mainSession);
        // Capture background overrides so we can confirm omitBackground toggles on/off.
        internalPage.mainSession.send = async (method, params) => {
            sendCalls.push({ method, params });
            return originalSend(method, params);
        };
        try {
            const maskCount = await maskLocator.count();
            console.log(`[screenshot-test] maskLocator.count=${maskCount}`);
            const buffer = await page.screenshot({
                animations: "disabled",
                caret: "hide",
                clip: { x: 0, y: 0, width: 200, height: 200 },
                mask: [maskLocator],
                maskColor: "rgba(255, 0, 0, 0.4)",
                omitBackground: true,
                path: tempPath,
                quality: 80,
                scale: "css",
                style: "body { border: 3px solid black; }",
                timeout: screenshotTimeout,
                type: "jpeg",
            });
            console.log(`[screenshot-test] screenshot returned bytes=${buffer.length} elapsed=${Date.now() - testStart}ms`);
            (0, test_1.expect)(Buffer.isBuffer(buffer)).toBeTruthy();
            (0, test_1.expect)(screenshotCalls.length).toBeGreaterThanOrEqual(1);
            console.log(`[screenshot-test] screenshotCalls=${screenshotCalls.length} evaluateCalls=${evaluateCalls.length} sendCalls=${sendCalls.length}`);
            const recorded = screenshotCalls[0]?.options ?? {};
            (0, test_1.expect)(recorded).toMatchObject({ type: "jpeg", quality: 80 });
            (0, test_1.expect)(recorded?.clip).toMatchObject({
                x: 0,
                y: 0,
                width: 200,
                height: 200,
            });
            if (typeof recorded?.scale === "number") {
                (0, test_1.expect)(recorded.scale).toBeGreaterThan(0);
                (0, test_1.expect)(recorded.scale).toBeLessThanOrEqual(2);
            }
            await fs_1.promises.stat(tempPath);
            const maskNodes = await page.evaluate(() => document.querySelectorAll("[data-stagehand-mask]").length);
            (0, test_1.expect)(maskNodes).toBe(0);
            const styleNodes = await page.evaluate(() => document.querySelectorAll("[data-stagehand-style]").length);
            (0, test_1.expect)(styleNodes).toBe(0);
            const backgroundCalls = sendCalls.filter((call) => call.method === "Emulation.setDefaultBackgroundColorOverride");
            (0, test_1.expect)(backgroundCalls.length).toBeGreaterThan(1);
            (0, test_1.expect)(backgroundCalls.some((call) => call.params &&
                typeof call.params === "object" &&
                "color" in call.params)).toBeTruthy();
            (0, test_1.expect)(backgroundCalls.some((call) => !call.params ||
                Object.keys(call.params).length === 0)).toBeTruthy();
            const cssArgs = evaluateCalls
                .map((entry) => {
                const value = entry.arg;
                return value?.css ?? null;
            })
                .filter((css) => typeof css === "string");
            const tokens = evaluateCalls
                .map((entry) => {
                const arg = entry.arg;
                return arg?.token ?? null;
            })
                .filter((token) => typeof token === "string");
            // Tokens include which helper injected the style (animations/caret/custom).
            (0, test_1.expect)(tokens.some((token) => token.includes("animations"))).toBeTruthy();
            (0, test_1.expect)(tokens.some((token) => token.includes("caret"))).toBeTruthy();
            (0, test_1.expect)(tokens.some((token) => token.includes("custom"))).toBeTruthy();
            // Custom style should bubble through so we check the actual CSS text.
            (0, test_1.expect)(cssArgs.some((css) => css.includes("border: 3px solid black"))).toBeTruthy();
            const maskCalls = evaluateCalls.filter((entry) => {
                const arg = entry.arg;
                return (arg &&
                    typeof arg === "object" &&
                    "rects" in arg);
            });
            (0, test_1.expect)(maskCalls.length).toBeGreaterThan(0);
            const rects = maskCalls[0]?.arg
                ?.rects;
            (0, test_1.expect)(Array.isArray(rects)).toBeTruthy();
            (0, test_1.expect)(rects.length).toBe(2);
        }
        finally {
            frame_js_1.Frame.prototype.screenshot = originalScreenshot;
            frame_js_1.Frame.prototype.evaluate = originalEvaluate;
            internalPage.mainSession.send = originalSend;
            await fs_1.promises.unlink(tempPath).catch(() => { });
        }
    });
    (0, test_1.test)("masks elements inside dialog top layer", async () => {
        const page = v3.context.pages()[0];
        const html = `
      <!doctype html>
      <html>
        <head>
          <meta charset="utf-8" />
          <style>
            dialog { padding: 16px; border: 2px solid #444; }
            #dialog-input { display: block; width: 160px; height: 32px; }
          </style>
        </head>
        <body>
          <dialog id="dialog">
            <label>Secret <input id="dialog-input" value="top-layer" /></label>
          </dialog>
          <script>
            const dialog = document.getElementById("dialog");
            if (dialog) {
              if (typeof dialog.showModal === "function") {
                try {
                  dialog.showModal();
                } catch {
                  dialog.setAttribute("open", "");
                }
              } else {
                dialog.setAttribute("open", "");
              }
            }
          </script>
        </body>
      </html>
    `;
        await page.goto("data:text/html," + encodeURIComponent(html));
        const targetId = page.targetId();
        const originalScreenshot = frame_js_1.Frame.prototype.screenshot;
        let dialogMaskCount = 0;
        frame_js_1.Frame.prototype.screenshot = async function screenshotSpy(options) {
            const frame = this;
            if (frame.pageId === targetId) {
                dialogMaskCount = await frame.evaluate(() => {
                    const dialog = document.querySelector("dialog[open]");
                    if (!dialog)
                        return 0;
                    return dialog.querySelectorAll("[data-stagehand-mask]").length;
                });
                return Buffer.from("stub-image");
            }
            return originalScreenshot.call(this, options);
        };
        try {
            await page.screenshot({
                mask: [page.locator("#dialog-input")],
            });
            (0, test_1.expect)(dialogMaskCount).toBeGreaterThan(0);
        }
        finally {
            frame_js_1.Frame.prototype.screenshot = originalScreenshot;
        }
    });
});
//# sourceMappingURL=page-screenshot.spec.js.map