From e5f37b003d93a17c2a9958697269b2d3228d8830 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 4 Mar 2026 23:06:35 -0800 Subject: tuning --- perf-test.ts | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 perf-test.ts (limited to 'perf-test.ts') diff --git a/perf-test.ts b/perf-test.ts new file mode 100644 index 00000000..082d9113 --- /dev/null +++ b/perf-test.ts @@ -0,0 +1,146 @@ +import { chromium } from "@playwright/test"; + +const BASE = "http://localhost:5173"; + +(async () => { + const browser = await chromium.launch({ headless: true }); + const context = await browser.newContext(); + const page = await context.newPage(); + + // Collect all network requests during navigation + const requests: { url: string; duration: number; size: number }[] = []; + + // Login as bob + console.log("=== Logging in as bob ==="); + await page.goto(`${BASE}/login`); + await page.fill('input[name="username"]', "bob"); + await page.fill('input[name="password"]', "pw"); + await page.click('button[type="submit"]'); + await page.waitForURL("**/bob", { timeout: 10000 }); + console.log("Logged in, at:", page.url()); + + // Wait for page to fully settle + await page.waitForTimeout(2000); + + // Now measure navigation to /invoice/lnurl + console.log("\n=== Navigating to /invoice/lnurl ==="); + + // Enable request interception to measure timings + const navRequests: { url: string; method: string; duration: number }[] = []; + + page.on("request", (req) => { + (req as any)._startTime = Date.now(); + }); + + page.on("response", (res) => { + const req = res.request(); + const start = (req as any)._startTime; + if (start) { + navRequests.push({ + url: req.url().replace(BASE, ""), + method: req.method(), + duration: Date.now() - start, + }); + } + }); + + // Take screenshot before navigation + await page.screenshot({ path: "/tmp/perf-before.png" }); + + const navStart = Date.now(); + + // Click the Receive button on the first account (custodial/spending) + const receiveBtn = page.locator('[data-testid="account-receive"]').first(); + const receiveHref = await receiveBtn.locator("..").getAttribute("href"); + console.log("Receive link href:", receiveHref); + + await receiveBtn.click(); + + // Wait for the invoice page to load + await page.waitForURL("**/invoice/**", { timeout: 10000 }); + const urlReady = Date.now() - navStart; + console.log(`URL changed in ${urlReady}ms`); + + // Wait for QR to appear + try { + await page.waitForSelector('.invoice img[src]', { timeout: 5000 }); + const qrReady = Date.now() - navStart; + console.log(`QR img element appeared in ${qrReady}ms`); + } catch { + console.log("QR img not found within 5s"); + } + + // Check if QR image has actually loaded (naturalWidth > 0) + try { + await page.waitForFunction( + () => { + const img = document.querySelector(".invoice img") as HTMLImageElement; + return img && img.naturalWidth > 0; + }, + { timeout: 5000 } + ); + const qrLoaded = Date.now() - navStart; + console.log(`QR image fully loaded in ${qrLoaded}ms`); + } catch { + console.log("QR image did not fully load within 5s"); + } + + const totalTime = Date.now() - navStart; + + await page.screenshot({ path: "/tmp/perf-after.png" }); + + console.log(`\nTotal navigation time: ${totalTime}ms`); + console.log("\n=== Network requests during navigation ==="); + for (const r of navRequests.sort((a, b) => b.duration - a.duration)) { + console.log(` ${r.method} ${r.url} - ${r.duration}ms`); + } + + // Now measure a second navigation (back and forth) to see cached behavior + console.log("\n=== Going back to profile ==="); + navRequests.length = 0; + await page.goBack(); + await page.waitForTimeout(1000); + + console.log("\n=== Second navigation to /invoice/lnurl ==="); + navRequests.length = 0; + const nav2Start = Date.now(); + await receiveBtn.click(); + await page.waitForURL("**/invoice/**", { timeout: 10000 }); + const url2Ready = Date.now() - nav2Start; + console.log(`URL changed in ${url2Ready}ms`); + + try { + await page.waitForFunction( + () => { + const img = document.querySelector(".invoice img") as HTMLImageElement; + return img && img.naturalWidth > 0; + }, + { timeout: 5000 } + ); + const qr2Loaded = Date.now() - nav2Start; + console.log(`QR image fully loaded in ${qr2Loaded}ms`); + } catch { + console.log("QR image did not fully load within 5s"); + } + + await page.screenshot({ path: "/tmp/perf-after2.png" }); + + console.log(`\nTotal 2nd navigation time: ${Date.now() - nav2Start}ms`); + console.log("\n=== Network requests during 2nd navigation ==="); + for (const r of navRequests.sort((a, b) => b.duration - a.duration)) { + console.log(` ${r.method} ${r.url} - ${r.duration}ms`); + } + + // Also capture performance entries + const perfEntries = await page.evaluate(() => { + return performance.getEntriesByType("navigation").map((e: any) => ({ + name: e.name, + duration: e.duration, + domContentLoaded: e.domContentLoadedEventEnd - e.startTime, + loadEvent: e.loadEventEnd - e.startTime, + })); + }); + console.log("\n=== Performance entries ===", perfEntries); + + await browser.close(); +})(); -- cgit v1.2.3