(
projectDir: string,
opts: {
samples: number;
at?: number[];
atTransitions: boolean;
maxTransitionSamples?: number;
timeout: number;
tolerance: number;
motion?: MotionSpec;
},
)
| 221 | } |
| 222 | |
| 223 | async function runLayoutAudit( |
| 224 | projectDir: string, |
| 225 | opts: { |
| 226 | samples: number; |
| 227 | at?: number[]; |
| 228 | atTransitions: boolean; |
| 229 | maxTransitionSamples?: number; |
| 230 | timeout: number; |
| 231 | tolerance: number; |
| 232 | motion?: MotionSpec; |
| 233 | }, |
| 234 | ): Promise<LayoutAuditResult> { |
| 235 | const { ensureBrowser } = await import("../browser/manager.js"); |
| 236 | const puppeteer = await import("puppeteer-core"); |
| 237 | const html = await bundleProjectHtml(projectDir); |
| 238 | const server = await serveStaticProjectHtml( |
| 239 | projectDir, |
| 240 | html, |
| 241 | "Failed to bind local layout audit server", |
| 242 | ); |
| 243 | let chromeBrowser: import("puppeteer-core").Browser | undefined; |
| 244 | |
| 245 | try { |
| 246 | const browser = await ensureBrowser(); |
| 247 | chromeBrowser = await puppeteer.default.launch({ |
| 248 | headless: true, |
| 249 | executablePath: browser.executablePath, |
| 250 | args: [ |
| 251 | "--no-sandbox", |
| 252 | "--disable-gpu", |
| 253 | "--disable-dev-shm-usage", |
| 254 | "--enable-webgl", |
| 255 | "--use-gl=angle", |
| 256 | "--use-angle=swiftshader", |
| 257 | ], |
| 258 | }); |
| 259 | |
| 260 | const page = await chromeBrowser.newPage(); |
| 261 | await page.setViewport({ width: 1920, height: 1080 }); |
| 262 | await page.goto(server.url, { waitUntil: "domcontentloaded", timeout: 10000 }); |
| 263 | await alignViewportToComposition(page, server.url); |
| 264 | await page |
| 265 | .waitForFunction(() => !!(window as unknown as { __timelines?: unknown }).__timelines, { |
| 266 | timeout: opts.timeout, |
| 267 | }) |
| 268 | .catch(() => {}); |
| 269 | await waitForFonts(page, 750); |
| 270 | await new Promise((resolveSettle) => setTimeout(resolveSettle, 250)); |
| 271 | |
| 272 | const duration = await getCompositionDuration(page); |
| 273 | const baseSamples = buildLayoutSampleTimes({ duration, samples: opts.samples, at: opts.at }); |
| 274 | let transitionSamples: number[] = []; |
| 275 | let transitionSamplesDropped = 0; |
| 276 | if (opts.atTransitions) { |
| 277 | const boundaries = await collectTweenBoundaries(page); |
| 278 | const transitions = buildTransitionSampleTimes({ |
| 279 | duration, |
| 280 | boundaries, |
no test coverage detected