| 1 | // Real Electron verification of bounded diagnostic capture and report enrichment. |
| 2 | import assert from "node:assert/strict"; |
| 3 | import { readFileSync, statSync, mkdirSync } from "node:fs"; |
| 4 | import { join, resolve } from "node:path"; |
| 5 | import { performanceFixture } from "./performance-fixture.mjs"; |
| 6 | |
| 7 | const fixture = await performanceFixture(true, { archiveWorker: true, benchmark: true }); |
| 8 | try { |
| 9 | const { page, app, temp } = fixture; |
| 10 | assert.equal(await page.evaluate(async () => (await fetch(location.href)).headers.get("Document-Policy")), null); |
| 11 | const initial = await app.evaluate(({ BrowserWindow }) => BrowserWindow.getAllWindows()[0].webContents.debugger.isAttached()); |
| 12 | assert.equal(initial, false, "ordinary monitoring does not attach a profiler"); |
| 13 | // Exercise the production startup/visibility grace period before a deliberate |
| 14 | // long task in the disposable document. |
| 15 | await page.waitForTimeout(16000); |
| 16 | await page.evaluate(() => { |
| 17 | window.fixtureLongTasks = []; |
| 18 | new PerformanceObserver((entries) => { window.fixtureLongTasks.push(...entries.getEntries().map((entry) => entry.duration)); }).observe({ entryTypes: ["longtask"] }); |
| 19 | // A DevTools Runtime.evaluate call is not a normal renderer task. Schedule |
| 20 | // work through the event loop so the browser's long-task observer sees it. |
| 21 | setTimeout(() => window.diagnosticFixture.burn(950), 0); |
| 22 | }); |
| 23 | try { await page.locator("#performance-report-prompt").waitFor(); } |
| 24 | catch (error) { |
| 25 | console.error(await page.evaluate(() => ({ focused: document.hasFocus(), visibility: document.visibilityState, longTasks: window.fixtureLongTasks }))); |
| 26 | throw error; |
| 27 | } |
| 28 | const copy = page.locator(".performance-report__copy").first(); |
| 29 | await page.evaluate(() => window.diagnosticFixture.run(5500)); |
| 30 | try { |
| 31 | await page.waitForFunction(() => document.querySelector(".performance-report__body")?.textContent.includes("CPU profile after trigger: captured"), null, { timeout: 12000 }); |
| 32 | } catch (error) { |
| 33 | console.error(await page.evaluate(() => ({ focused: document.hasFocus(), status: document.querySelector(".performance-report__body")?.textContent.match(/CPU profile after trigger:[^\n]*/)?.[0] }))); |
| 34 | throw error; |
| 35 | } |
| 36 | const report = await page.locator(".performance-report__body").innerText(); |
| 37 | // CDP sampling may legitimately return no user frames on a short profile. |
| 38 | // A captured result proves the archived analysis worker completed; frame |
| 39 | // filtering itself is covered deterministically by profileAnalysis.test.ts. |
| 40 | assert.match(report, /process samples: Electron only/); |
| 41 | assert.doesNotMatch(report, new RegExp(temp.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))); |
| 42 | assert.equal(await app.evaluate(({ BrowserWindow }) => BrowserWindow.getAllWindows()[0].webContents.debugger.isAttached()), false); |
| 43 | await page.evaluate(() => Object.defineProperty(navigator, "clipboard", { value: { writeText: async (text) => { window.fixtureCopiedText = text; } }, configurable: true })); |
| 44 | await copy.click(); |
| 45 | await page.waitForFunction(() => window.fixtureCopiedText?.includes("CPU profile after trigger: captured")); |
| 46 | const result = await page.evaluate(() => window.reasonixDesktop.native.exportHeapSnapshot()); |
| 47 | assert.equal(result.status, "saved"); |
| 48 | const heap = join(temp, "fixture.heapsnapshot"); |
| 49 | assert.ok(statSync(heap).size > 1000); |
| 50 | assert.ok(JSON.parse(readFileSync(heap, "utf8")).snapshot); |
| 51 | const artifacts = resolve(import.meta.dirname, "../artifacts/performance"); |
| 52 | mkdirSync(artifacts, { recursive: true }); |
| 53 | await page.screenshot({ path: join(artifacts, "diagnostic-report.png") }); |
| 54 | console.log("PASS: no eager profiling; automatic bounded capture; ASAR analysis Worker; live copy; debugger released; local heap snapshot"); |
| 55 | } finally { await fixture.close(); } |
| 56 |