fix(web-probe): 适配持续连接页面截图
This commit is contained in:
@@ -129,6 +129,7 @@ description: UniDesk Web 开发与受控浏览器验证技能。用户提到 Web
|
||||
- 再用 `web-probe screenshot` 从 owning YAML 选择的固定公网入口打开该 session 深链,观察用户可见的消息、终态和语义化错误;
|
||||
- 不把 WebProbe 的硬编码 DOM selector、长等待、临时 Playwright 脚本或探针自身超时作为 Workbench 业务终态的唯一门禁;探针异常时保留其作为工具证据,并以 CLI 终态和截图继续归因;
|
||||
- CLI 必须走同一产品 dispatcher 和身份语义,禁止以 direct manager、snapshot、result polling 或其他第二业务路径替代。
|
||||
- 一次性截图默认按 `domcontentloaded` 导航,持续 SSE 页面不得改用 `networkidle`;完整边界见 [references/web-probe.md](references/web-probe.md)。
|
||||
- Native 阶段采用短反馈循环:
|
||||
- 修改后先执行最小语法检查和目标功能验证;
|
||||
- 使用 `web-probe` 从 YAML 选择的固定 HTTPS 或公网 HTTP 入口验证真实 DOM、交互、布局和截图;custom/local 仅记录为 preflight,不能宣称 L1 通过;
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
- 交互流程使用 `observe start`、`observe command`、`observe collect` 和 `observe analyze`;
|
||||
- `run` 或 `script` 只用于有界探测和 smoke;
|
||||
- 收尾证据引用 observer id、command id、artifact/report SHA、screenshot SHA 和关键有界字段。
|
||||
- 一次性截图的导航完成条件:
|
||||
- `web-probe screenshot` 默认使用 `domcontentloaded`,适用于持续 SSE、WebSocket 和长轮询页面;
|
||||
- 只有页面能够稳定进入网络静默时,才显式传 `--wait-until networkidle`;
|
||||
- 需要业务 DOM 或交互判定时继续使用 typed readiness 或 observer,不能把导航完成当作业务通过。
|
||||
- L1 native Web 公网 readiness:
|
||||
- 使用 `bun scripts/cli.ts web-probe native-readiness --node <node> --lane <lane> --profile <profile>`;
|
||||
- 固定 HTTPS origin 来自 lane 的 `nativeDevelopment.publicExposure`,页面与判据来自 `nativeReadinessProfiles`;
|
||||
|
||||
@@ -182,12 +182,14 @@ export function hwlabNodeWebProbeScreenshotHelp(): Record<string, unknown> {
|
||||
"--path": "附加到 semantic origin 的绝对路径;与 --url 互斥。",
|
||||
"--url": "custom/local 一次性 URL,必须包含完整路径;与 --origin、--path 互斥。",
|
||||
"--viewport": "截图视口,默认 1440x900。",
|
||||
"--wait-until": "导航完成条件,默认 domcontentloaded;静态页面可显式选择 networkidle。",
|
||||
"--selector": "等待指定 selector 后再截图。",
|
||||
"--full-page|--no-full-page": "选择整页或当前视口截图。",
|
||||
},
|
||||
notes: [
|
||||
"使用 --url 时不要再传 --path;custom/local URL 已包含完整路径。",
|
||||
"internal/public/native 运行面必须通过 owning YAML 的 --origin 选择,禁止用手写 URL 或 IP 替代。",
|
||||
"持续 SSE、WebSocket 或长轮询页面使用默认 domcontentloaded;networkidle 只用于能够稳定进入网络静默的页面。",
|
||||
"该命令只采集证据,不修改浏览器能力、资源门禁或运行面。",
|
||||
],
|
||||
mutation: false,
|
||||
|
||||
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
|
||||
import { test } from "bun:test";
|
||||
|
||||
import { hwlabNodeWebProbeHelp, hwlabNodeWebProbeScreenshotHelp } from "../hwlab-node-help";
|
||||
import { parseNodeWebProbeOptions } from "./web-probe";
|
||||
|
||||
test("web-probe help promotes reusable Workbench debug and product Trace commands", () => {
|
||||
const help = hwlabNodeWebProbeHelp();
|
||||
@@ -21,5 +22,18 @@ test("web-probe screenshot scoped help explains semantic, custom and local URL u
|
||||
assert.match(usage, /--url 'https:\/\/custom\.example\/workbench'/u);
|
||||
assert.match(usage, /--url 'http:\/\/127\.0\.0\.1:4173\/workbench'/u);
|
||||
assert.match(notes, /--url.*不要再传 --path/u);
|
||||
assert.match(notes, /持续 SSE.*domcontentloaded/u);
|
||||
assert.equal(help.mutation, false);
|
||||
});
|
||||
|
||||
test("web-probe screenshot defaults to DOM readiness and keeps network idle explicit", () => {
|
||||
const defaults = parseNodeWebProbeOptions(["screenshot", "--node", "NC01", "--lane", "v03", "--origin", "native"]);
|
||||
assert.equal(defaults.action, "screenshot");
|
||||
if (defaults.action !== "screenshot") throw new Error("expected screenshot options");
|
||||
assert.equal(defaults.waitUntil, "domcontentloaded");
|
||||
|
||||
const explicit = parseNodeWebProbeOptions(["screenshot", "--node", "NC01", "--lane", "v03", "--origin", "native", "--wait-until", "networkidle"]);
|
||||
assert.equal(explicit.action, "screenshot");
|
||||
if (explicit.action !== "screenshot") throw new Error("expected screenshot options");
|
||||
assert.equal(explicit.waitUntil, "networkidle");
|
||||
});
|
||||
|
||||
@@ -1105,7 +1105,7 @@ const screenshotPath = process.env.UNIDESK_WEB_PROBE_SCREENSHOT_PATH;
|
||||
const width = Number(process.env.UNIDESK_WEB_PROBE_SCREENSHOT_WIDTH || 1440);
|
||||
const height = Number(process.env.UNIDESK_WEB_PROBE_SCREENSHOT_HEIGHT || 900);
|
||||
const timeout = Number(process.env.UNIDESK_WEB_PROBE_SCREENSHOT_TIMEOUT_MS || 30000);
|
||||
const waitUntil = process.env.UNIDESK_WEB_PROBE_SCREENSHOT_WAIT_UNTIL || "networkidle";
|
||||
const waitUntil = process.env.UNIDESK_WEB_PROBE_SCREENSHOT_WAIT_UNTIL || "domcontentloaded";
|
||||
const fullPage = process.env.UNIDESK_WEB_PROBE_SCREENSHOT_FULL_PAGE === "1";
|
||||
const selector = process.env.UNIDESK_WEB_PROBE_SCREENSHOT_SELECTOR || "";
|
||||
const executablePath = process.env.UNIDESK_WEB_PROBE_SCREENSHOT_EXECUTABLE_PATH || "";
|
||||
|
||||
@@ -2296,7 +2296,7 @@ export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
|
||||
localDir: optionValue(args, "--local-dir") ?? "/tmp",
|
||||
name: parseWebProbeScreenshotName(optionValue(args, "--name") ?? `web-probe-${node.toLowerCase()}-${lane}.png`),
|
||||
timeoutMs,
|
||||
waitUntil: parseWebProbeScreenshotWaitUntil(optionValue(args, "--wait-until") ?? "networkidle"),
|
||||
waitUntil: parseWebProbeScreenshotWaitUntil(optionValue(args, "--wait-until") ?? "domcontentloaded"),
|
||||
fullPage: args.includes("--full-page") && !args.includes("--no-full-page"),
|
||||
selector: optionValue(args, "--selector") ?? null,
|
||||
keepRemote: args.includes("--keep-remote"),
|
||||
|
||||
Reference in New Issue
Block a user