Skip to main content
Glama

Chrome DevTools MCP

Official
performance.test.ts9.19 kB
/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import {describe, it, afterEach} from 'node:test'; import assert from 'assert'; import sinon from 'sinon'; import { analyzeInsight, startTrace, stopTrace, } from '../../src/tools/performance.js'; import {withBrowser} from '../utils.js'; import {loadTraceAsBuffer} from '../trace-processing/fixtures/load.js'; import { parseRawTraceBuffer, TraceResult, traceResultIsSuccess, } from '../../src/trace-processing/parse.js'; describe('performance', () => { afterEach(() => { sinon.restore(); }); describe('performance_start_trace', () => { it('starts a trace recording', async () => { await withBrowser(async (response, context) => { context.setIsRunningPerformanceTrace(false); const selectedPage = context.getSelectedPage(); const startTracingStub = sinon.stub(selectedPage.tracing, 'start'); await startTrace.handler( {params: {reload: true, autoStop: false}}, response, context, ); sinon.assert.calledOnce(startTracingStub); assert.ok(context.isRunningPerformanceTrace()); assert.ok( response.responseLines .join('\n') .match(/The performance trace is being recorded/), ); }); }); it('can navigate to about:blank and record a page reload', async () => { await withBrowser(async (response, context) => { const selectedPage = context.getSelectedPage(); sinon.stub(selectedPage, 'url').callsFake(() => 'https://www.test.com'); const gotoStub = sinon.stub(selectedPage, 'goto'); const startTracingStub = sinon.stub(selectedPage.tracing, 'start'); await startTrace.handler( {params: {reload: true, autoStop: false}}, response, context, ); sinon.assert.calledOnce(startTracingStub); sinon.assert.calledWithExactly(gotoStub, 'about:blank', { waitUntil: ['networkidle0'], }); sinon.assert.calledWithExactly(gotoStub, 'https://www.test.com', { waitUntil: ['load'], }); assert.ok(context.isRunningPerformanceTrace()); assert.ok( response.responseLines .join('\n') .match(/The performance trace is being recorded/), ); }); }); it('can autostop and store a recording', async () => { const rawData = loadTraceAsBuffer('basic-trace.json.gz'); await withBrowser(async (response, context) => { const selectedPage = context.getSelectedPage(); sinon.stub(selectedPage, 'url').callsFake(() => 'https://www.test.com'); sinon.stub(selectedPage, 'goto').callsFake(() => Promise.resolve(null)); const startTracingStub = sinon.stub(selectedPage.tracing, 'start'); const stopTracingStub = sinon .stub(selectedPage.tracing, 'stop') .callsFake(async () => { return rawData; }); const clock = sinon.useFakeTimers(); const handlerPromise = startTrace.handler( {params: {reload: true, autoStop: true}}, response, context, ); // In the handler we wait 5 seconds after the page load event (which is // what DevTools does), hence we now fake-progress time to allow // the handler to complete. We allow extra time because the Trace // Engine also uses some timers to yield updates and we need those to // execute. await clock.tickAsync(6_000); await handlerPromise; clock.restore(); sinon.assert.calledOnce(startTracingStub); sinon.assert.calledOnce(stopTracingStub); assert.strictEqual( context.isRunningPerformanceTrace(), false, 'Tracing was stopped', ); assert.strictEqual(context.recordedTraces().length, 1); assert.ok( response.responseLines .join('\n') .match(/The performance trace has been stopped/), ); }); }); it('errors if a recording is already active', async () => { await withBrowser(async (response, context) => { context.setIsRunningPerformanceTrace(true); const selectedPage = context.getSelectedPage(); const startTracingStub = sinon.stub(selectedPage.tracing, 'start'); await startTrace.handler( {params: {reload: true, autoStop: false}}, response, context, ); sinon.assert.notCalled(startTracingStub); assert.ok( response.responseLines .join('\n') .match(/a performance trace is already running/), ); }); }); }); describe('performance_analyze_insight', () => { async function parseTrace(fileName: string): Promise<TraceResult> { const rawData = loadTraceAsBuffer(fileName); const result = await parseRawTraceBuffer(rawData); if (!traceResultIsSuccess(result)) { assert.fail(`Unexpected trace parse error: ${result.error}`); } return result; } it('returns the information on the insight', async t => { const trace = await parseTrace('web-dev-with-commit.json.gz'); await withBrowser(async (response, context) => { context.storeTraceRecording(trace); context.setIsRunningPerformanceTrace(false); await analyzeInsight.handler( { params: { insightName: 'LCPBreakdown', }, }, response, context, ); t.assert.snapshot(response.responseLines.join('\n')); }); }); it('returns an error if the insight does not exist', async () => { const trace = await parseTrace('web-dev-with-commit.json.gz'); await withBrowser(async (response, context) => { context.storeTraceRecording(trace); context.setIsRunningPerformanceTrace(false); await analyzeInsight.handler( { params: { insightName: 'MadeUpInsightName', }, }, response, context, ); assert.ok( response.responseLines .join('\n') .match(/No Insight with the name MadeUpInsightName found./), ); }); }); it('returns an error if no trace has been recorded', async () => { await withBrowser(async (response, context) => { await analyzeInsight.handler( { params: { insightName: 'LCPBreakdown', }, }, response, context, ); assert.ok( response.responseLines .join('\n') .match( /No recorded traces found. Record a performance trace so you have Insights to analyze./, ), ); }); }); }); describe('performance_stop_trace', () => { it('does nothing if the trace is not running and does not error', async () => { await withBrowser(async (response, context) => { context.setIsRunningPerformanceTrace(false); await stopTrace.handler({params: {}}, response, context); }); }); it('will stop the trace and return trace info when a trace is running', async () => { const rawData = loadTraceAsBuffer('basic-trace.json.gz'); await withBrowser(async (response, context) => { context.setIsRunningPerformanceTrace(true); const selectedPage = context.getSelectedPage(); const stopTracingStub = sinon .stub(selectedPage.tracing, 'stop') .callsFake(async () => { return rawData; }); await stopTrace.handler({params: {}}, response, context); assert.ok( response.responseLines.includes( 'The performance trace has been stopped.', ), ); assert.strictEqual(context.recordedTraces().length, 1); sinon.assert.calledOnce(stopTracingStub); }); }); it('returns an error message if parsing the trace buffer fails', async t => { await withBrowser(async (response, context) => { context.setIsRunningPerformanceTrace(true); const selectedPage = context.getSelectedPage(); sinon .stub(selectedPage.tracing, 'stop') .returns(Promise.resolve(undefined)); await stopTrace.handler({params: {}}, response, context); t.assert.snapshot(response.responseLines.join('\n')); }); }); it('returns the high level summary of the performance trace', async t => { const rawData = loadTraceAsBuffer('web-dev-with-commit.json.gz'); await withBrowser(async (response, context) => { context.setIsRunningPerformanceTrace(true); const selectedPage = context.getSelectedPage(); sinon.stub(selectedPage.tracing, 'stop').callsFake(async () => { return rawData; }); await stopTrace.handler({params: {}}, response, context); t.assert.snapshot(response.responseLines.join('\n')); }); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ChromeDevTools/chrome-devtools-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server