mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 12:54:29 +00:00
feat(cli): run-ID telemetry correlation and check breakdown event
HYPERFRAMES_RUN_ID (trimmed, 128-char cap) attaches as run_id to the generic cli_command / cli_command_result events, absent when unset, so an orchestrator setting it per design element can group a verify loop's invocations in analytics. check additionally emits one check_report event per invocation (including lint-short-circuited and failing runs): gate booleans, per-class error/warning counts, launch/seek/contrast phase timings, sample counts, ok and exit code. Timings stay internal; no command output changes.
This commit is contained in:
@@ -44,6 +44,11 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
it("carries raw browser geometry through the page driver and pipeline", async () => {
|
||||
vi.spyOn(Date, "now")
|
||||
.mockReturnValueOnce(100)
|
||||
.mockReturnValueOnce(160)
|
||||
.mockReturnValueOnce(200)
|
||||
.mockReturnValueOnce(240);
|
||||
document.body.innerHTML = `
|
||||
<div data-composition-id="main" data-duration="10" data-width="640" data-height="360">
|
||||
<section data-composition-file="scenes/hero.html">
|
||||
@@ -85,6 +90,7 @@ it("carries raw browser geometry through the page driver and pipeline", async ()
|
||||
time: 5,
|
||||
}),
|
||||
]);
|
||||
expect(result.timings).toEqual({ launchSettleMs: 60, seekLoopMs: 40, contrastMs: 0 });
|
||||
expect(mocks.serverClose).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ export async function runBrowserCheck(
|
||||
let chromeBrowser: import("puppeteer-core").Browser | undefined;
|
||||
|
||||
try {
|
||||
const launchSettleStart = Date.now();
|
||||
const session = await openSettledCompositionPage(html, server.url, {
|
||||
renderReadyTimeoutMs: options.timeout,
|
||||
renderReadyWarningSuffix: "checking the current page state",
|
||||
@@ -104,12 +105,14 @@ export async function runBrowserCheck(
|
||||
await waitForPreferredSeekTarget(page, 500);
|
||||
|
||||
const rootAnchor = await resolveRootAnchor(page);
|
||||
const launchSettleMs = Date.now() - launchSettleStart;
|
||||
const driver = createPageDriver(page, (time) => {
|
||||
currentTime = time;
|
||||
});
|
||||
const result = await runGrid(driver, options, motion);
|
||||
return {
|
||||
...result,
|
||||
timings: { ...result.timings, launchSettleMs },
|
||||
runtimeFindings: drafts.map((draft) => runtimeFinding(draft, rootAnchor)),
|
||||
};
|
||||
} finally {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import { join, relative } from "node:path";
|
||||
import { trackCheckReport } from "../telemetry/events.js";
|
||||
import { getRunId } from "../telemetry/runId.js";
|
||||
import type { ProjectDir } from "./project.js";
|
||||
import { lintProject, shouldBlockRender, type ProjectLintResult } from "./lintProject.js";
|
||||
import {
|
||||
@@ -180,6 +182,7 @@ interface GridSamples {
|
||||
motionFrames: MotionFrame[];
|
||||
contrastEntries: ContrastAuditEntry[];
|
||||
screenshots: CheckScreenshot[];
|
||||
contrastMs: number;
|
||||
}
|
||||
|
||||
interface GeometrySeen {
|
||||
@@ -349,6 +352,7 @@ async function collectGridSamples(
|
||||
motionFrames: [],
|
||||
contrastEntries: [],
|
||||
screenshots: [],
|
||||
contrastMs: 0,
|
||||
};
|
||||
for (const time of mergeSampleTimes(grid.layoutSamples, motion.times)) {
|
||||
await driver.seek(time);
|
||||
@@ -366,7 +370,9 @@ async function collectGridSamples(
|
||||
);
|
||||
}
|
||||
if (contrastSet.has(time)) {
|
||||
const contrastStart = Date.now();
|
||||
const capture = await driver.collectContrast(time);
|
||||
collected.contrastMs += Date.now() - contrastStart;
|
||||
collected.contrastEntries.push(...capture.entries);
|
||||
collected.screenshots.push({ time, pngBase64: capture.pngBase64 });
|
||||
}
|
||||
@@ -382,7 +388,9 @@ export async function runAuditGrid(
|
||||
await driver.initialize(options.contrast);
|
||||
const grid = await buildSampleGrid(driver, options);
|
||||
const plan = await planMotionSampling(driver, motion, grid.duration);
|
||||
const seekLoopStart = Date.now();
|
||||
const collected = await collectGridSamples(driver, options, grid, plan);
|
||||
const seekLoopMs = Date.now() - seekLoopStart;
|
||||
|
||||
let motionIssues = plan.preflightIssues;
|
||||
if (motion.kind === "valid" && motionIssues.length === 0 && collected.motionFrames.length > 0) {
|
||||
@@ -408,6 +416,7 @@ export async function runAuditGrid(
|
||||
contrastChecked: collected.contrastEntries.length,
|
||||
contrastPassed: contrast.passed,
|
||||
screenshots: collected.screenshots,
|
||||
timings: { launchSettleMs: 0, seekLoopMs, contrastMs: collected.contrastMs },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -555,7 +564,7 @@ function buildReport(
|
||||
layout.errorCount +
|
||||
motionSection.errorCount +
|
||||
contrastSection.errorCount;
|
||||
return {
|
||||
const report: CheckReport = {
|
||||
ok: errorCount === 0 && (!options.strict || warningCount === 0),
|
||||
strict: options.strict,
|
||||
lint,
|
||||
@@ -580,6 +589,32 @@ function buildReport(
|
||||
times: options.snapshots ? browser.screenshots.map((shot) => shot.time) : [],
|
||||
},
|
||||
};
|
||||
trackCheckReport({
|
||||
contrastGate: options.contrast,
|
||||
motionGate: motion.kind !== "none",
|
||||
captionZoneGate: options.captionZone !== undefined,
|
||||
frameCheckGate: options.frameCheck !== undefined,
|
||||
snapshotsGate: options.snapshots,
|
||||
lintErrors: lint.errorCount,
|
||||
lintWarnings: lint.warningCount,
|
||||
runtimeErrors: runtime.errorCount,
|
||||
runtimeWarnings: runtime.warningCount,
|
||||
layoutErrors: layout.errorCount,
|
||||
layoutWarnings: layout.warningCount,
|
||||
motionErrors: motionSection.errorCount,
|
||||
motionWarnings: motionSection.warningCount,
|
||||
contrastErrors: contrastSection.errorCount,
|
||||
contrastWarnings: contrastSection.warningCount,
|
||||
launchSettleMs: browser.timings.launchSettleMs,
|
||||
seekLoopMs: browser.timings.seekLoopMs,
|
||||
contrastMs: browser.timings.contrastMs,
|
||||
gridPoints: browser.layoutSamples.length,
|
||||
contrastPoints: browser.contrastChecked,
|
||||
ok: report.ok,
|
||||
exitCode: checkExitCode(report),
|
||||
runId: getRunId(),
|
||||
});
|
||||
return report;
|
||||
}
|
||||
|
||||
function shapeLayoutSection(
|
||||
@@ -651,6 +686,7 @@ function emptyBrowserResult(): CheckBrowserResult {
|
||||
contrastChecked: 0,
|
||||
contrastPassed: 0,
|
||||
screenshots: [],
|
||||
timings: { launchSettleMs: 0, seekLoopMs: 0, contrastMs: 0 },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +133,12 @@ export interface CheckScreenshot {
|
||||
pngBase64: string;
|
||||
}
|
||||
|
||||
export interface CheckTimings {
|
||||
launchSettleMs: number;
|
||||
seekLoopMs: number;
|
||||
contrastMs: number;
|
||||
}
|
||||
|
||||
export interface CheckBrowserResult {
|
||||
duration: number;
|
||||
layoutSamples: number[];
|
||||
@@ -147,6 +153,7 @@ export interface CheckBrowserResult {
|
||||
contrastChecked: number;
|
||||
contrastPassed: number;
|
||||
screenshots: CheckScreenshot[];
|
||||
timings: CheckTimings;
|
||||
}
|
||||
|
||||
/** The seek-grid audit loop, injected into checkBrowser so it never imports checkPipeline back. */
|
||||
|
||||
Reference in New Issue
Block a user