fix(web-probe): wait for mdtodo DOM before sentinel screenshots
This commit is contained in:
@@ -23,16 +23,20 @@ sentinel:
|
||||
path: /projects/mdtodo/sources/constart-71freq-mdtodo/files/file_0db4dc4e46adf188/tasks/R1
|
||||
- type: screenshot
|
||||
label: mdtodo-desktop-few-task-gap
|
||||
waitProjectManagementReady: true
|
||||
- type: goto
|
||||
path: /projects/mdtodo/sources/constart-71freq-mdtodo/files/file_5f9645ffe8774b92/tasks/R14
|
||||
- type: screenshot
|
||||
label: mdtodo-r14-selected
|
||||
waitProjectManagementReady: true
|
||||
- type: openMdtodoReportPreview
|
||||
task: R14
|
||||
link: R14
|
||||
- type: screenshot
|
||||
label: mdtodo-r14-report-preview
|
||||
waitProjectManagementReady: true
|
||||
- type: toggleMdtodoReportFullscreen
|
||||
text: toggle
|
||||
- type: screenshot
|
||||
label: mdtodo-r14-report-fullscreen
|
||||
waitProjectManagementReady: true
|
||||
|
||||
@@ -390,7 +390,7 @@ async function processCommand(command) {
|
||||
case "deleteMdtodoTask": return deleteMdtodoTask(command);
|
||||
case "launchWorkbenchFromTask": return withObserverSync(await launchWorkbenchFromTask(command), "launchWorkbenchFromTask");
|
||||
case "launchWorkbenchFromMdtodo": return withObserverSync(await launchWorkbenchFromMdtodo(command), "launchWorkbenchFromMdtodo");
|
||||
case "screenshot": return captureScreenshot(command.reason || "manual", command.imageType || "png");
|
||||
case "screenshot": return captureCommandScreenshot(command);
|
||||
case "mark": return { mark: truncate(command.label || command.text || "mark", 200), currentUrl: currentPageUrl(), pageId };
|
||||
case "stop": stopping = true; return { stopping: true, currentUrl: currentPageUrl(), pageId };
|
||||
default: throw new Error("unsupported observer command type: " + command.type);
|
||||
@@ -1251,6 +1251,28 @@ async function projectManagementReadinessSnapshot(targetPage) {
|
||||
}, { selectors }).catch((error) => ({ error: errorSummary(error), valuesRedacted: true }));
|
||||
}
|
||||
|
||||
async function waitForProjectManagementCommandReady(options = {}) {
|
||||
const timeoutMs = Number.isFinite(Number(options.timeoutMs)) ? Math.max(1, Number(options.timeoutMs)) : 15000;
|
||||
const started = Date.now();
|
||||
const deadline = started + timeoutMs;
|
||||
let last = null;
|
||||
while (Date.now() <= deadline) {
|
||||
last = await projectManagementCommandSnapshot();
|
||||
const path = String(last?.path || safeUrlPath(currentPageUrl()) || "");
|
||||
const baseReady = last?.pageKind === "project-management-mdtodo"
|
||||
&& Number(last?.sourceCount || 0) > 0
|
||||
&& Number(last?.fileCount || 0) > 0
|
||||
&& Number(last?.taskCount || 0) > 0;
|
||||
const needsTask = /\/tasks\//u.test(path);
|
||||
const taskReady = !needsTask || Boolean(last?.selectedTaskId || last?.selectedTaskRef?.hash || last?.taskBodyVisible === true || last?.launchButtonVisible === true);
|
||||
const needsReport = /\/reports\//u.test(path);
|
||||
const reportReady = !needsReport || last?.reportPreviewVisible === true || last?.reportFullscreenVisible === true || Number(last?.reportLinkCount || 0) > 0;
|
||||
if (baseReady && taskReady && reportReady) return { ok: true, reason: "project-management-command-ready", durationMs: Date.now() - started, snapshot: last, valuesRedacted: true };
|
||||
await page.waitForTimeout(250).catch(() => {});
|
||||
}
|
||||
return { ok: false, reason: "project-management-command-not-ready", durationMs: Date.now() - started, snapshot: last, valuesRedacted: true };
|
||||
}
|
||||
|
||||
function isWorkbenchPathname(value) {
|
||||
const pathname = String(value || "");
|
||||
return pathname === "/workbench" || pathname === "/workspace" || pathname.startsWith("/workbench/") || pathname.startsWith("/workspace/");
|
||||
@@ -3793,6 +3815,18 @@ async function captureScreenshot(reason, imageType = "png") {
|
||||
return artifact;
|
||||
}
|
||||
|
||||
async function captureCommandScreenshot(command) {
|
||||
const shouldWaitProject = command.waitProjectManagementReady === true;
|
||||
const readiness = shouldWaitProject ? await waitForProjectManagementCommandReady({ timeoutMs: 15000 }) : null;
|
||||
if (readiness && readiness.ok !== true) {
|
||||
const error = new Error("screenshot project-management readiness wait failed: " + (readiness.reason || "not-ready"));
|
||||
error.details = { readiness, currentUrl: currentPageUrl(), pageId, valuesRedacted: true };
|
||||
throw error;
|
||||
}
|
||||
const artifact = await captureScreenshot(command.reason || command.label || "manual", command.imageType || "png");
|
||||
return { ...artifact, readiness, valuesRedacted: true };
|
||||
}
|
||||
|
||||
function eventRecord(type, data) {
|
||||
const clean = sanitize(data) || {};
|
||||
return { ts: new Date().toISOString(), type, jobId, pageId: clean.pageId ?? pageId, pageRole: clean.pageRole ?? "control", sampleSeq, commandId: activeCommandId, ...clean };
|
||||
@@ -3838,6 +3872,7 @@ function commandInputSummary(command) {
|
||||
expectedSentinelRange: command.expectedSentinelRange || null,
|
||||
expectedActionWaitMs: command.expectedActionWaitMs === null || command.expectedActionWaitMs === undefined || command.expectedActionWaitMs === "" ? null : Number(command.expectedActionWaitMs),
|
||||
requireComposerReady: command.requireComposerReady === true,
|
||||
waitProjectManagementReady: command.waitProjectManagementReady === true,
|
||||
findingId: command.findingId || null,
|
||||
blocking: command.blocking === true ? true : command.blocking === false ? false : null,
|
||||
sourceId: opaque(command.sourceId),
|
||||
|
||||
@@ -2574,6 +2574,7 @@ function appendScenarioObserveCommandArgs(args: string[], item: Record<string, u
|
||||
const text = stringAtNullable(item, "text") ?? stringAtNullable(item, "value");
|
||||
if (text !== null) args.push("--text", text);
|
||||
}
|
||||
if (item.waitProjectManagementReady === true && !args.includes("--wait-project-management-ready")) args.push("--wait-project-management-ready");
|
||||
}
|
||||
|
||||
function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
|
||||
|
||||
@@ -204,6 +204,7 @@ export interface NodeWebProbeObserveOptions {
|
||||
commandExpectedSentinelRange: string | null;
|
||||
commandExpectedActionWaitMs: number | null;
|
||||
commandRequireComposerReady: boolean;
|
||||
commandWaitProjectManagementReady: boolean;
|
||||
commandFindingId: string | null;
|
||||
commandBlocking: boolean | null;
|
||||
commandAccountId: string | null;
|
||||
|
||||
@@ -278,7 +278,7 @@ export function parseNodeWebProbeObserveOptions(
|
||||
"--workspace-root",
|
||||
"--workspace-root-ref",
|
||||
"--root",
|
||||
]), new Set(["--force", "--full", "--raw", "--text-stdin", "--require-composer-ready", "--blocking", "--non-blocking"]));
|
||||
]), new Set(["--force", "--full", "--raw", "--text-stdin", "--require-composer-ready", "--wait-project-management-ready", "--blocking", "--non-blocking"]));
|
||||
const commandTypeRaw = optionValue(args, "--type") ?? null;
|
||||
const commandType = commandTypeRaw === null ? null : parseNodeWebProbeObserveCommandType(commandTypeRaw);
|
||||
const stateDir = optionValue(args, "--state-dir") ?? indexed?.stateDir ?? null;
|
||||
@@ -431,6 +431,7 @@ export function parseNodeWebProbeObserveOptions(
|
||||
commandExpectedSentinelRange,
|
||||
commandExpectedActionWaitMs,
|
||||
commandRequireComposerReady: args.includes("--require-composer-ready"),
|
||||
commandWaitProjectManagementReady: args.includes("--wait-project-management-ready"),
|
||||
commandFindingId,
|
||||
commandBlocking,
|
||||
commandAccountId,
|
||||
@@ -1500,6 +1501,7 @@ export function runNodeWebProbeObserveCommand(options: NodeWebProbeObserveOption
|
||||
expectedSentinelRange: options.commandExpectedSentinelRange,
|
||||
expectedActionWaitMs: options.commandExpectedActionWaitMs,
|
||||
requireComposerReady: options.commandRequireComposerReady,
|
||||
waitProjectManagementReady: options.commandWaitProjectManagementReady,
|
||||
findingId: options.commandFindingId,
|
||||
blocking: options.commandBlocking,
|
||||
accountId: options.commandAccountId,
|
||||
|
||||
Reference in New Issue
Block a user