|
|
@@ -20,7 +20,9 @@ export function summarizeNavigationMilestones(samples: NavigationMilestoneSample
|
|
|
}
|
|
|
return {
|
|
|
samples: samples.length,
|
|
|
- milestones: Object.fromEntries(names.map((name) => [name, summarize((sample) => sample.milestones[name] === true)])),
|
|
|
+ milestones: Object.fromEntries(
|
|
|
+ names.map((name) => [name, summarize((sample) => sample.milestones[name] === true)]),
|
|
|
+ ),
|
|
|
all: summarize((sample) => names.every((name) => sample.milestones[name] === true)),
|
|
|
}
|
|
|
}
|
|
|
@@ -38,75 +40,78 @@ export async function measureNavigationMilestones(
|
|
|
navigate: () => Promise<void>
|
|
|
},
|
|
|
) {
|
|
|
- await page.evaluate(({ triggerSelector, milestones }) => {
|
|
|
- const samples: NavigationMilestoneSample[] = []
|
|
|
- const streaks = new Map<string, number>()
|
|
|
- const marked = new Set<string>()
|
|
|
- let started: number | undefined
|
|
|
- let running = true
|
|
|
- const visible = (selector: string) =>
|
|
|
- [...document.querySelectorAll<HTMLElement>(selector)].some((element) => {
|
|
|
- const rect = element.getBoundingClientRect()
|
|
|
- const style = getComputedStyle(element)
|
|
|
- return rect.width > 0 && rect.height > 0 && style.visibility !== "hidden" && style.display !== "none"
|
|
|
- })
|
|
|
- const sample = () => {
|
|
|
- if (!running || started === undefined) return
|
|
|
- requestAnimationFrame(() => {
|
|
|
- setTimeout(() => {
|
|
|
- if (!running || started === undefined) return
|
|
|
- const current = Object.fromEntries(
|
|
|
- Object.entries(milestones).map(([name, milestone]) => [
|
|
|
- name,
|
|
|
- milestone.visible === false ? !document.querySelector(milestone.selector) : visible(milestone.selector),
|
|
|
- ]),
|
|
|
- )
|
|
|
- samples.push({
|
|
|
- observedAtMs: performance.now() - started,
|
|
|
- milestones: current,
|
|
|
- })
|
|
|
- Object.entries(current).forEach(([name, value]) => {
|
|
|
- if (!value) {
|
|
|
- streaks.set(name, 0)
|
|
|
- return
|
|
|
+ await page.evaluate(
|
|
|
+ ({ triggerSelector, milestones }) => {
|
|
|
+ const samples: NavigationMilestoneSample[] = []
|
|
|
+ const streaks = new Map<string, number>()
|
|
|
+ const marked = new Set<string>()
|
|
|
+ let started: number | undefined
|
|
|
+ let running = true
|
|
|
+ const visible = (selector: string) =>
|
|
|
+ [...document.querySelectorAll<HTMLElement>(selector)].some((element) => {
|
|
|
+ const rect = element.getBoundingClientRect()
|
|
|
+ const style = getComputedStyle(element)
|
|
|
+ return rect.width > 0 && rect.height > 0 && style.visibility !== "hidden" && style.display !== "none"
|
|
|
+ })
|
|
|
+ const sample = () => {
|
|
|
+ if (!running || started === undefined) return
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ setTimeout(() => {
|
|
|
+ if (!running || started === undefined) return
|
|
|
+ const current = Object.fromEntries(
|
|
|
+ Object.entries(milestones).map(([name, milestone]) => [
|
|
|
+ name,
|
|
|
+ milestone.visible === false ? !document.querySelector(milestone.selector) : visible(milestone.selector),
|
|
|
+ ]),
|
|
|
+ )
|
|
|
+ samples.push({
|
|
|
+ observedAtMs: performance.now() - started,
|
|
|
+ milestones: current,
|
|
|
+ })
|
|
|
+ Object.entries(current).forEach(([name, value]) => {
|
|
|
+ if (!value) {
|
|
|
+ streaks.set(name, 0)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!marked.has(`${name}.first`)) {
|
|
|
+ performance.mark(`opencode.navigation.${name}.first`)
|
|
|
+ marked.add(`${name}.first`)
|
|
|
+ }
|
|
|
+ const streak = (streaks.get(name) ?? 0) + 1
|
|
|
+ streaks.set(name, streak)
|
|
|
+ if (streak === 3) performance.mark(`opencode.navigation.${name}.stable`)
|
|
|
+ })
|
|
|
+ const all = Object.values(current).every(Boolean)
|
|
|
+ const allStreak = all ? (streaks.get("all") ?? 0) + 1 : 0
|
|
|
+ streaks.set("all", allStreak)
|
|
|
+ if (all && !marked.has("all.first")) {
|
|
|
+ performance.mark("opencode.navigation.all.first")
|
|
|
+ marked.add("all.first")
|
|
|
}
|
|
|
- if (!marked.has(`${name}.first`)) {
|
|
|
- performance.mark(`opencode.navigation.${name}.first`)
|
|
|
- marked.add(`${name}.first`)
|
|
|
- }
|
|
|
- const streak = (streaks.get(name) ?? 0) + 1
|
|
|
- streaks.set(name, streak)
|
|
|
- if (streak === 3) performance.mark(`opencode.navigation.${name}.stable`)
|
|
|
- })
|
|
|
- const all = Object.values(current).every(Boolean)
|
|
|
- const allStreak = all ? (streaks.get("all") ?? 0) + 1 : 0
|
|
|
- streaks.set("all", allStreak)
|
|
|
- if (all && !marked.has("all.first")) {
|
|
|
- performance.mark("opencode.navigation.all.first")
|
|
|
- marked.add("all.first")
|
|
|
- }
|
|
|
- if (allStreak === 3) performance.mark("opencode.navigation.all.stable")
|
|
|
+ if (allStreak === 3) performance.mark("opencode.navigation.all.stable")
|
|
|
+ sample()
|
|
|
+ }, 0)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ document.addEventListener(
|
|
|
+ "click",
|
|
|
+ (event) => {
|
|
|
+ if (!(event.target instanceof Element) || !event.target.closest(triggerSelector)) return
|
|
|
+ started = performance.now()
|
|
|
+ performance.mark("opencode.navigation.click")
|
|
|
sample()
|
|
|
- }, 0)
|
|
|
- })
|
|
|
- }
|
|
|
- document.addEventListener(
|
|
|
- "click",
|
|
|
- (event) => {
|
|
|
- if (!(event.target instanceof Element) || !event.target.closest(triggerSelector)) return
|
|
|
- started = performance.now()
|
|
|
- performance.mark("opencode.navigation.click")
|
|
|
- sample()
|
|
|
- },
|
|
|
- { capture: true, once: true },
|
|
|
- )
|
|
|
- ;(window as Window & { __navigationMilestones?: NavigationMilestoneProbe }).__navigationMilestones = {
|
|
|
- samples,
|
|
|
- stop: () => {
|
|
|
- running = false
|
|
|
- },
|
|
|
- }
|
|
|
- }, { triggerSelector: input.triggerSelector, milestones: input.milestones })
|
|
|
+ },
|
|
|
+ { capture: true, once: true },
|
|
|
+ )
|
|
|
+ ;(window as Window & { __navigationMilestones?: NavigationMilestoneProbe }).__navigationMilestones = {
|
|
|
+ samples,
|
|
|
+ stop: () => {
|
|
|
+ running = false
|
|
|
+ },
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { triggerSelector: input.triggerSelector, milestones: input.milestones },
|
|
|
+ )
|
|
|
await input.navigate()
|
|
|
await page.waitForFunction(() => {
|
|
|
const samples = (window as Window & { __navigationMilestones?: NavigationMilestoneProbe }).__navigationMilestones
|