Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"browser-probe-assertions-smoke": "tsx scripts/browser-probe-assertions-smoke.ts",
"browser-probe-context-smoke": "tsx scripts/browser-probe-context-smoke.ts",
"browser-probe-profile-matrix-smoke": "tsx scripts/browser-probe-profile-matrix-smoke.ts",
"browser-lifecycle-observer-smoke": "tsx scripts/browser-lifecycle-observer-smoke.ts",
"browser-probe-pre-page-script-smoke": "tsx scripts/browser-probe-pre-page-script-smoke.ts",
"browser-actions-artifact-smoke": "tsx scripts/browser-actions-artifact-smoke.ts",
"browser-scenario-artifact-smoke": "tsx scripts/browser-scenario-artifact-smoke.ts",
Expand Down
1 change: 1 addition & 0 deletions packages/runtime-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export interface ArtifactReviewBrowserSummary {
consoleMessages: number
errors: number
html?: string
lifecycle?: string
network?: string
networkEvents?: number
checkpoints?: string
Expand Down
46 changes: 45 additions & 1 deletion packages/runtime-playground/src/browser-artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface BrowserProbeArtifact {
console?: string
errors?: string
html?: string
lifecycle?: string
memory?: string
network?: string
performance?: string
Expand All @@ -39,6 +40,7 @@ export interface BrowserProbeArtifact {
errors: number
finalUrl: string
htmlSnapshot: boolean
lifecycle?: BrowserProbeLifecycleSummary
memory?: BrowserProbeMemorySummary
metrics?: Record<string, number>
networkEvents: number
Expand Down Expand Up @@ -151,6 +153,44 @@ export interface BrowserProbeMemorySummary {
jsEventListeners: BrowserProbeMetricDigest
}

export interface BrowserProbeLifecycleMetric {
selector: string
first_seen_ms: number | null
first_visible_ms: number | null
first_child_ms: number | null
first_iframe_ms: number | null
first_visible_iframe_ms: number | null
first_button_ms: number | null
first_visible_button_ms: number | null
stable_visible_ms: number | null
removed_count: number
peak_child_count: number
peak_iframe_count: number
peak_visible_iframe_count: number
peak_button_count: number
peak_visible_button_count: number
final_child_count: number
final_iframe_count: number
final_visible_iframe_count: number
final_button_count: number
final_visible_button_count: number
}

export interface BrowserProbeLifecycleSummary {
schema: "wp-codebox/browser-lifecycle/v1"
version: 1
startedAtMs: number
selectors: Record<string, BrowserProbeLifecycleMetric>
}

export interface BrowserProbeLifecycleArtifact {
schema: "wp-codebox/browser-lifecycle/v1"
version: 1
capturedAt: string
startedAtMs: number
selectors: Record<string, BrowserProbeLifecycleMetric>
}

export interface BrowserProbePerformanceSummary {
resources: number
transferSizeBytes: number
Expand Down Expand Up @@ -336,6 +376,7 @@ export function browserReviewSummary(probes: BrowserProbeArtifact[]): ArtifactRe
consoleMessages: probe.summary.consoleMessages,
errors: probe.summary.errors,
html: probe.files.html,
lifecycle: probe.files.lifecycle,
network: probe.files.network,
networkEvents: probe.summary.networkEvents,
screenshot: probe.files.screenshot,
Expand Down Expand Up @@ -383,6 +424,9 @@ export function browserManifestFiles(artifactRoot: string, probes: BrowserProbeA
if (probe.files.html) {
files.set(probe.files.html, { kind: "browser-html-snapshot", contentType: "text/html; charset=utf-8" })
}
if (probe.files.lifecycle) {
files.set(probe.files.lifecycle, { kind: "browser-lifecycle", contentType: "application/json" })
}
if (probe.files.memory) {
files.set(probe.files.memory, { kind: "browser-memory", contentType: "application/json" })
}
Expand All @@ -402,6 +446,6 @@ export function browserManifestFiles(artifactRoot: string, probes: BrowserProbeA
}

export function browserRedactionPaths(probe: BrowserProbeArtifact): string[] {
return [probe.files.steps, probe.files.actions, probe.files.editorState, probe.files.checkpoints, probe.files.console, probe.files.errors, probe.files.html, probe.files.memory, probe.files.network, probe.files.performance, probe.files.summary]
return [probe.files.steps, probe.files.actions, probe.files.editorState, probe.files.checkpoints, probe.files.console, probe.files.errors, probe.files.html, probe.files.lifecycle, probe.files.memory, probe.files.network, probe.files.performance, probe.files.summary]
.filter((path): path is string => typeof path === "string" && path.length > 0)
}
29 changes: 23 additions & 6 deletions packages/runtime-playground/src/browser-command-runners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { mkdir, readFile, writeFile } from "node:fs/promises"
import { join } from "node:path"
import { assertRuntimeCommandAllowed, browserInteractionScriptUsesEvaluate, type ExecutionSpec, type RuntimeCreateSpec } from "@automattic/wp-codebox-core"
import { browserInteractionStepsFromArgs, durationStringMs } from "./browser-actions.js"
import type { BrowserProbeArtifact, BrowserProbeCapabilityDiagnostics, BrowserProbeCheckpointRecord, BrowserProbeContextDetails, BrowserProbeErrorRecord, BrowserProbeMemoryArtifact, BrowserProbeNetworkRecord, BrowserProbePerformanceArtifact, BrowserProbeScriptMetadata, BrowserProbeViewport, BrowserStepRecord } from "./browser-artifacts.js"
import type { BrowserProbeArtifact, BrowserProbeCapabilityDiagnostics, BrowserProbeCheckpointRecord, BrowserProbeContextDetails, BrowserProbeErrorRecord, BrowserProbeLifecycleArtifact, BrowserProbeMemoryArtifact, BrowserProbeNetworkRecord, BrowserProbePerformanceArtifact, BrowserProbeScriptMetadata, BrowserProbeViewport, BrowserStepRecord } from "./browser-artifacts.js"
import { browserAssertionsSummary, browserStepRecord, executeBrowserInteractionStep } from "./browser-interactions.js"
import { browserProbeLifecycleArtifact, browserProbeLifecycleInitScript, collectBrowserProbeLifecycle } from "./browser-lifecycle.js"
import { browserProbeBenchMetrics, jsonLines, serializeBrowserConsoleMessage, serializeBrowserError, serializeBrowserFinishedRequest, serializeBrowserRequestFailure } from "./browser-metrics.js"
import { BROWSER_PROBE_CAPTURE_VALUES, BROWSER_PROBE_PERFORMANCE_INIT_SCRIPT, BROWSER_PROBE_STATE_INIT_SCRIPT, browserProbeAssertionsFromArgs, browserProbeAssertionsNeedMetrics, browserProbeAssertionsNeedNetwork, browserProbeCheckpoint, browserProbeMemoryArtifact, browserProbePendingCheckpoints, browserProbePerformanceArtifact, browserProbeReplayability, browserProbeViewport, executeBrowserProbeAssertions, navigateBrowserProbe } from "./browser-probe.js"
import { argValue, cleanWpCliOutput, commaListArg } from "./commands.js"
Expand Down Expand Up @@ -66,7 +67,7 @@ export async function runBrowserProbeCommand({
}: {
artifactRoot: string
command?: string
runtimeSpec: RuntimeCreateSpec
runtimeSpec?: RuntimeCreateSpec
server: PlaygroundCliServer
spec: ExecutionSpec
}): Promise<{ artifact: BrowserProbeArtifact; artifacts?: BrowserProbeArtifact[]; output: string }> {
Expand Down Expand Up @@ -128,7 +129,7 @@ async function runSingleBrowserProbeCommand({
}: {
artifactRoot: string
command: string
runtimeSpec: RuntimeCreateSpec
runtimeSpec?: RuntimeCreateSpec
server: PlaygroundCliServer
spec: ExecutionSpec
browserFilesDirectory: string
Expand Down Expand Up @@ -163,6 +164,7 @@ async function runSingleBrowserProbeCommand({
const script = argValue(args, "script")
const failFast = booleanArg(args, "fail-fast", false)
const stallTimeoutMs = durationArg(args, "stall-timeout", 0)
const lifecycleSelectors = commaListArg(args, "observe")
const assertions = browserProbeAssertionsFromArgs(args)
const capturesConsoleForAssertions = assertions.some((assertion) => assertion.type === "no-console-errors" || assertion.type === "no-errors")
const capturesErrorsForAssertions = assertions.some((assertion) => assertion.type === "no-page-errors" || assertion.type === "no-errors")
Expand All @@ -184,6 +186,7 @@ async function runSingleBrowserProbeCommand({
const errorsPath = join(browserDirectory, "errors.jsonl")
const htmlPath = join(browserDirectory, "snapshot.html")
const memoryPath = join(browserDirectory, "memory.json")
const lifecyclePath = join(browserDirectory, "lifecycle.json")
const networkPath = join(browserDirectory, "network.jsonl")
const performancePath = join(browserDirectory, "performance.json")
const screenshotPath = join(browserDirectory, "screenshot.png")
Expand All @@ -204,6 +207,7 @@ async function runSingleBrowserProbeCommand({
let screenshotSha256: string | undefined
let viewport: BrowserProbeViewport | null = null
let scriptResult: unknown
let lifecycleArtifact: BrowserProbeLifecycleArtifact | undefined
let memoryArtifact: BrowserProbeMemoryArtifact | undefined
let performanceArtifact: BrowserProbePerformanceArtifact | undefined
let page: import("playwright").Page | null = null
Expand Down Expand Up @@ -231,6 +235,9 @@ async function runSingleBrowserProbeCommand({
await page.setViewportSize(requestedViewport)
}
await page.addInitScript(BROWSER_PROBE_STATE_INIT_SCRIPT)
if (lifecycleSelectors.length > 0) {
await page.addInitScript(browserProbeLifecycleInitScript(lifecycleSelectors))
}
if (capturesBrowserMetrics) {
await page.addInitScript(BROWSER_PROBE_PERFORMANCE_INIT_SCRIPT)
}
Expand Down Expand Up @@ -321,6 +328,10 @@ async function runSingleBrowserProbeCommand({
performanceArtifact = browserProbePerformanceArtifact(checkpoints)
}
}
const lifecycle = lifecycleSelectors.length > 0 ? await collectBrowserProbeLifecycle(page) : undefined
if (lifecycle) {
lifecycleArtifact = browserProbeLifecycleArtifact(lifecycle)
}

if (capture.has("html")) {
try {
Expand Down Expand Up @@ -360,6 +371,9 @@ async function runSingleBrowserProbeCommand({
if (memoryArtifact) {
await writeFile(memoryPath, `${JSON.stringify(memoryArtifact, null, 2)}\n`)
}
if (lifecycleArtifact) {
await writeFile(lifecyclePath, `${JSON.stringify(lifecycleArtifact, null, 2)}\n`)
}
if (performanceArtifact) {
await writeFile(performancePath, `${JSON.stringify(performanceArtifact, null, 2)}\n`)
}
Expand All @@ -386,6 +400,7 @@ async function runSingleBrowserProbeCommand({
...(checkpoints.length > 0 ? { checkpoints: `${browserFilesDirectory}/checkpoints.jsonl` } : {}),
...(capture.has("errors") || capturesErrorsForAssertions ? { errors: `${browserFilesDirectory}/errors.jsonl` } : {}),
...(capture.has("html") ? { html: `${browserFilesDirectory}/snapshot.html` } : {}),
...(lifecycleArtifact ? { lifecycle: `${browserFilesDirectory}/lifecycle.json` } : {}),
...(memoryArtifact ? { memory: `${browserFilesDirectory}/memory.json` } : {}),
...(capture.has("network") || capturesNetworkForAssertions ? { network: `${browserFilesDirectory}/network.jsonl` } : {}),
...(performanceArtifact ? { performance: `${browserFilesDirectory}/performance.json` } : {}),
Expand All @@ -398,6 +413,7 @@ async function runSingleBrowserProbeCommand({
errors: errors.length,
finalUrl,
htmlSnapshot: capture.has("html"),
...(lifecycleArtifact ? { lifecycle: { schema: lifecycleArtifact.schema, version: lifecycleArtifact.version, startedAtMs: lifecycleArtifact.startedAtMs, selectors: lifecycleArtifact.selectors } } : {}),
...(memoryArtifact ? { memory: memoryArtifact.peak } : {}),
...(memoryArtifact || performanceArtifact ? { metrics: browserProbeBenchMetrics(memoryArtifact, performanceArtifact) } : {}),
networkEvents: network.length,
Expand All @@ -418,6 +434,7 @@ async function runSingleBrowserProbeCommand({
finalUrl,
waitFor,
durationMs,
...(lifecycleSelectors.length > 0 ? { observe: lifecycleSelectors } : {}),
failFast,
stallTimeoutMs,
capture: [...capture].sort(),
Expand Down Expand Up @@ -1784,11 +1801,11 @@ function now(): string {
return new Date().toISOString()
}

function browserProbePreviewOrigins(runtimeSpec: RuntimeCreateSpec, localPreviewOrigin: string): { localPreviewOrigin: string; requestedPreviewOrigin?: string; effectivePreviewOrigin: string } {
function browserProbePreviewOrigins(runtimeSpec: RuntimeCreateSpec | undefined, localPreviewOrigin: string): { localPreviewOrigin: string; requestedPreviewOrigin?: string; effectivePreviewOrigin: string } {
return {
localPreviewOrigin,
requestedPreviewOrigin: runtimeSpec.preview?.publicUrl,
effectivePreviewOrigin: runtimeSpec.preview?.publicUrl ?? localPreviewOrigin,
requestedPreviewOrigin: runtimeSpec?.preview?.publicUrl,
effectivePreviewOrigin: runtimeSpec?.preview?.publicUrl ?? localPreviewOrigin,
}
}

Expand Down
Loading