From 93bbc89c7adfba9d580c433dd07b19ab0235838b Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 16 Jul 2026 19:58:24 +0900 Subject: [PATCH] chore(dev): tweak frontend browser diagnostics --- .github/scripts/chrome.mts | 58 ++++------- .github/scripts/frontend-browser-report.mts | 18 ++-- .../scripts/frontend-browser-report.test.mts | 98 ------------------- .github/workflows/lint.yml | 1 - 4 files changed, 24 insertions(+), 151 deletions(-) delete mode 100644 .github/scripts/frontend-browser-report.test.mts diff --git a/.github/scripts/chrome.mts b/.github/scripts/chrome.mts index 2be10bd261..b40c82a263 100644 --- a/.github/scripts/chrome.mts +++ b/.github/scripts/chrome.mts @@ -90,53 +90,19 @@ export type TabMemory = { totalBytes: number; }; -export type BrowserConsoleMetrics = { - log: number; - warn: number; - error: number; - info: number; -}; - export type BrowserDiagnostics = { pageErrorCount: number; - console: BrowserConsoleMetrics; + console: Record<'log' | 'warning' | 'error' | 'info', number>; }; -export function createBrowserDiagnostics(): BrowserDiagnostics { - return { - pageErrorCount: 0, - console: { - log: 0, - warn: 0, - error: 0, - info: 0, - }, - }; -} - -export function recordPageError(diagnostics: BrowserDiagnostics) { - diagnostics.pageErrorCount += 1; -} - -function isBrowserConsoleMetricKey(value: string): value is keyof BrowserConsoleMetrics { - return value === 'log' || value === 'warn' || value === 'error' || value === 'info'; -} - -export function recordConsoleMessageType(diagnostics: BrowserDiagnostics, messageType: string) { - const metricKey = messageType === 'warning' ? 'warn' : messageType; - if (!isBrowserConsoleMetricKey(metricKey)) return; - diagnostics.console[metricKey] += 1; -} - export function summarizeBrowserDiagnostics(samples: BrowserDiagnostics[]): BrowserDiagnostics { - if (samples.length === 0) throw new Error('No browser diagnostic samples'); const median = (select: (sample: BrowserDiagnostics) => number) => util.median(samples.map(select)); return { pageErrorCount: median(sample => sample.pageErrorCount), console: { log: median(sample => sample.console.log), - warn: median(sample => sample.console.warn), + warning: median(sample => sample.console.warning), error: median(sample => sample.console.error), info: median(sample => sample.console.info), }, @@ -204,7 +170,10 @@ type PlaywrightBrowserOptions = { export class HeadlessChromeController { public networkRequests: NetworkRequest[] = []; public webSocketConnections: WebSocketConnection[] = []; - private readonly diagnostics = createBrowserDiagnostics(); + private readonly diagnostics = { + pageErrorCount: 0, + console: {} as Record, + }; private readonly browser: Browser; private readonly context: BrowserContext; public readonly page: Page; @@ -225,10 +194,14 @@ export class HeadlessChromeController { this.page.setDefaultTimeout(options.scenarioTimeoutMs); this.page.setDefaultNavigationTimeout(options.scenarioTimeoutMs); this.page.on('pageerror', () => { - recordPageError(this.diagnostics); + this.diagnostics.pageErrorCount++; }); this.page.on('console', message => { - recordConsoleMessageType(this.diagnostics, message.type()); + if (this.diagnostics.console[message.type()] == null) { + this.diagnostics.console[message.type()] = 1; + } else { + this.diagnostics.console[message.type()]++; + } }); } @@ -441,7 +414,12 @@ export class HeadlessChromeController { public collectDiagnostics(): BrowserDiagnostics { return { pageErrorCount: this.diagnostics.pageErrorCount, - console: { ...this.diagnostics.console }, + console: { + log: this.diagnostics.console.log ?? 0, + warning: this.diagnostics.console.warning ?? 0, + error: this.diagnostics.console.error ?? 0, + info: this.diagnostics.console.info ?? 0, + }, }; } diff --git a/.github/scripts/frontend-browser-report.mts b/.github/scripts/frontend-browser-report.mts index 829041f1d0..cfb37ccf3b 100644 --- a/.github/scripts/frontend-browser-report.mts +++ b/.github/scripts/frontend-browser-report.mts @@ -172,19 +172,8 @@ function getMetric(report: BrowserMeasurement, key: string) { return report.performance.cdpMetrics[key]; } -export function renderBrowserDiagnosticsRows(base: BrowserMetricsReport, head: BrowserMetricsReport, all = false) { - return [ - metricRow('Page errors', base, head, summary => summary.diagnostics.pageErrorCount, sample => sample.diagnostics.pageErrorCount, util.formatNumber, 1, !all), - metricRow('Console log', base, head, summary => summary.diagnostics.console.log, sample => sample.diagnostics.console.log, util.formatNumber, 1, !all), - metricRow('Console warnings', base, head, summary => summary.diagnostics.console.warn, sample => sample.diagnostics.console.warn, util.formatNumber, 1, !all), - metricRow('Console errors', base, head, summary => summary.diagnostics.console.error, sample => sample.diagnostics.console.error, util.formatNumber, 1, !all), - metricRow('Console info', base, head, summary => summary.diagnostics.console.info, sample => sample.diagnostics.console.info, util.formatNumber, 1, !all), - ].filter(row => row != null); -} - function renderSummaryTable(base: BrowserMetricsReport, head: BrowserMetricsReport, all = false) { const rows = [ - ...renderBrowserDiagnosticsRows(base, head, all), //metricRow('Scenario duration', base, head, summary => summary.durationMs, sample => sample.durationMs, formatMs), metricRow('Requests', base, head, summary => summary.network.requestCount, sample => sample.network.requestCount, util.formatNumber, 1, !all), //metricRow('Failed requests', base, head, summary => summary.network.failedRequestCount, sample => sample.network.failedRequestCount, util.formatNumber), @@ -215,7 +204,12 @@ function renderSummaryTable(base: BrowserMetricsReport, head: BrowserMetricsRepo metricRow('WebSocket connections', base, head, summary => summary.network.webSocketConnectionCount, sample => sample.network.webSocketConnectionCount, util.formatNumber, 1, !all), metricRow('WebSocket sent', base, head, summary => summary.network.webSocketSentBytes, sample => sample.network.webSocketSentBytes, util.formatBytes, 10000, !all), metricRow('WebSocket received', base, head, summary => summary.network.webSocketReceivedBytes, sample => sample.network.webSocketReceivedBytes, util.formatBytes, 10000, !all), - metricRow('Tab memory', base, head, summary => summary.performance.tabMemory.totalBytes, sample => sample.performance.tabMemory.totalBytes, util.formatBytes, 10000, !all), + metricRow('Page errors', base, head, summary => summary.diagnostics.pageErrorCount, sample => sample.diagnostics.pageErrorCount, util.formatNumber, 1, !all), + metricRow('Console log', base, head, summary => summary.diagnostics.console.log, sample => sample.diagnostics.console.log, util.formatNumber, 1, !all), + metricRow('Console warnings', base, head, summary => summary.diagnostics.console.warn, sample => sample.diagnostics.console.warn, util.formatNumber, 1, !all), + metricRow('Console errors', base, head, summary => summary.diagnostics.console.error, sample => sample.diagnostics.console.error, util.formatNumber, 1, !all), + metricRow('Console info', base, head, summary => summary.diagnostics.console.info, sample => sample.diagnostics.console.info, util.formatNumber, 1, !all), + metricRow('Page-attributed memory', base, head, summary => summary.performance.tabMemory.totalBytes, sample => sample.performance.tabMemory.totalBytes, util.formatBytes, 10000, !all), ].filter(row => row != null); return [ diff --git a/.github/scripts/frontend-browser-report.test.mts b/.github/scripts/frontend-browser-report.test.mts deleted file mode 100644 index 4d4f130504..0000000000 --- a/.github/scripts/frontend-browser-report.test.mts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import assert from 'node:assert/strict'; -import test from 'node:test'; -import { - createBrowserDiagnostics, - recordConsoleMessageType, - recordPageError, - summarizeBrowserDiagnostics, - type BrowserDiagnostics, -} from './chrome.mts'; -import { - renderBrowserDiagnosticsRows, - type BrowserMeasurement, - type BrowserMeasurementSample, - type BrowserMetricsReport, -} from './frontend-browser-report.mts'; - -function diagnostics(pageErrorCount: number, log: number, warn: number, error: number, info: number): BrowserDiagnostics { - return { - pageErrorCount, - console: { log, warn, error, info }, - }; -} - -function browserReport(label: 'base' | 'head', values: BrowserDiagnostics[]): BrowserMetricsReport { - const samples = values.map((value, index) => ({ - round: index + 1, - diagnostics: value, - } as BrowserMeasurementSample)); - - return { - label, - timestamp: '2026-07-16T00:00:00.000Z', - url: 'http://127.0.0.1:61812', - scenario: 'test', - sampleCount: samples.length, - aggregation: 'median', - summary: { - diagnostics: summarizeBrowserDiagnostics(values), - } as BrowserMeasurement, - samples, - }; -} - -test('records page errors and only the requested console message types', () => { - const result = createBrowserDiagnostics(); - recordPageError(result); - recordPageError(result); - for (const type of ['log', 'warning', 'error', 'info', 'debug', 'trace']) { - recordConsoleMessageType(result, type); - } - - assert.deepEqual(result, diagnostics(2, 1, 1, 1, 1)); -}); - -test('summarizes browser diagnostics with a median for every counter', () => { - const result = summarizeBrowserDiagnostics([ - diagnostics(0, 10, 20, 30, 40), - diagnostics(4, 14, 24, 34, 44), - diagnostics(2, 12, 22, 32, 42), - diagnostics(3, 13, 23, 33, 43), - diagnostics(1, 11, 21, 31, 41), - ]); - - assert.deepEqual(result, diagnostics(2, 12, 22, 32, 42)); -}); - -test('renders each changed browser diagnostics metric as a separate row', () => { - const base = browserReport('base', Array.from({ length: 5 }, () => diagnostics(0, 0, 0, 0, 0))); - const head = browserReport('head', Array.from({ length: 5 }, () => diagnostics(1, 1, 1, 1, 1))); - const rows = renderBrowserDiagnosticsRows(base, head).join('\n'); - - for (const label of ['Page errors', 'Console log', 'Console warnings', 'Console errors', 'Console info']) { - assert.match(rows, new RegExp(`\\*\\*${label}\\*\\*`)); - } -}); - -test('renders browser diagnostics deltas from paired rounds instead of summary medians', () => { - const base = browserReport('base', [0, 9, 10, 100, 101].map(value => diagnostics(value, 0, 0, 0, 0))); - const head = browserReport('head', [10, 100, 101, 0, 9].map(value => diagnostics(value, 0, 0, 0, 0))); - const rows = renderBrowserDiagnosticsRows(base, head); - - assert.equal(rows.length, 1); - assert.match(rows[0], /\*\*Page errors\*\*/); - assert.match(rows[0], /\\text\{\+10\}/); -}); - -test('omits browser diagnostics rows whose paired median does not change', () => { - const values = Array.from({ length: 5 }, () => diagnostics(2, 3, 4, 5, 6)); - const base = browserReport('base', values); - const head = browserReport('head', values); - - assert.deepEqual(renderBrowserDiagnosticsRows(base, head), []); -}); diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c1a3aeda6e..63f8684c33 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -152,4 +152,3 @@ jobs: node-version-file: '.node-version' - run: node --test .github/scripts/frontend-js-size.test.mts - run: node --test .github/scripts/memory-stability-util.test.mts - - run: node --test .github/scripts/frontend-browser-report.test.mts