#!/usr/bin/env node import fs from "node:fs"; import path from "node:path"; import { createRequire } from "node:module"; const require = createRequire(new URL("../frontend/package.json", import.meta.url)); const { chromium } = require("playwright"); const baseUrl = String(process.env.IMAGEFIND_LIVE_URL || "").replace(/\/$/, ""); const tokenFile = process.env.IMAGEFIND_LIVE_TOKEN_FILE || ""; const outputDir = path.resolve(process.env.IMAGEFIND_LIVE_OUTPUT || "dist/postdeploy-0.5.45-bulk-retry"); if (!baseUrl || !tokenFile) throw new Error("IMAGEFIND_LIVE_URL and IMAGEFIND_LIVE_TOKEN_FILE are required"); const token = fs.readFileSync(tokenFile, "utf8").trim(); fs.mkdirSync(outputDir, { recursive: true }); async function openTasks(browser, viewport) { const context = await browser.newContext({ viewport, locale: "zh-CN" }); const origin = new URL(baseUrl).origin; await context.route("**/*", async route => { const url = new URL(route.request().url()); if (url.origin === origin && url.pathname.includes("/api/")) { await route.continue({ headers: { ...route.request().headers(), authorization: `Bearer ${token}` }, }); return; } await route.continue(); }); const page = await context.newPage(); await page.goto(baseUrl, { waitUntil: "domcontentloaded", timeout: 30_000 }); await page.locator(".app-shell").waitFor({ timeout: 30_000 }); if (viewport.width <= 720) { await page.getByRole("button", { name: "更多" }).click(); await page.locator(".mobile-more-menu").getByRole("button", { name: "设置" }).click(); } else { await page.locator(".nav-item-8").click(); } await page.getByRole("heading", { name: "设置" }).waitFor({ timeout: 20_000 }); await page.getByRole("button", { name: "任务与偏好" }).click(); await page.locator(".jobs-panel").waitFor({ timeout: 20_000 }); await page .getByRole("navigation", { name: "后台任务通道" }) .getByRole("button", { name: "上传转存" }) .click(); await page.locator(".job-retry-all").waitFor({ timeout: 20_000 }); return { context, page }; } const browser = await chromium.launch({ headless: true }); const report = {}; try { { const { context, page } = await openTasks(browser, { width: 390, height: 844 }); const button = page.locator(".job-retry-all"); const box = await button.boundingBox(); report.mobile = { button: (await button.innerText()).replace(/\s+/g, " ").trim(), min_touch_target: Boolean(box && box.height >= 44), overflow: await page.evaluate(() => document.documentElement.scrollWidth - innerWidth), }; await page.screenshot({ path: path.join(outputDir, "mobile-transfer-retry.png"), fullPage: true }); await context.close(); } { const { context, page } = await openTasks(browser, { width: 1440, height: 900 }); const button = page.locator(".job-retry-all"); report.desktop = { button: (await button.innerText()).replace(/\s+/g, " ").trim() }; await button.click(); const dialog = page.getByRole("alertdialog"); report.desktop.confirmation = (await dialog.innerText()).replace(/\s+/g, " ").trim(); await page.screenshot({ path: path.join(outputDir, "desktop-confirmation.png"), fullPage: true }); const retryResponse = page.waitForResponse( response => response.url().includes("/api/v1/jobs/retry-failed") && response.request().method() === "POST", ); await dialog.getByRole("button", { name: "继续" }).click(); const response = await retryResponse; if (!response.ok()) throw new Error(`bulk retry failed: ${response.status()} ${await response.text()}`); report.desktop.result = await response.json(); await page.waitForTimeout(1_000); report.desktop.button_visible_after_retry = (await page.locator(".job-retry-all").count()) > 0; await page.screenshot({ path: path.join(outputDir, "desktop-result.png"), fullPage: true }); await context.close(); } } finally { await browser.close(); } fs.writeFileSync(path.join(outputDir, "report.json"), `${JSON.stringify(report, null, 2)}\n`); console.log(JSON.stringify(report, null, 2));