chromedevtools--chrome-devtools-mcp
264 行
7.6 KiB
TypeScript
264 行
7.6 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import assert from 'node:assert';
|
|
import {describe, it} from 'node:test';
|
|
|
|
import sinon from 'sinon';
|
|
|
|
import type {ExtensionServiceWorker} from '../../src/McpContext.js';
|
|
import type {ElementHandle, Frame} from '../../src/third_party/index.js';
|
|
import {evaluateScriptTool} from '../../src/tools/script.js';
|
|
import {serverHooks} from '../server.js';
|
|
import {html, withMcpContext} from '../utils.js';
|
|
|
|
const evaluateScript = evaluateScriptTool(true);
|
|
|
|
describe('script', () => {
|
|
const server = serverHooks();
|
|
|
|
describe('browser_evaluate_script', () => {
|
|
it('evaluates', async () => {
|
|
await withMcpContext(async (response, context) => {
|
|
await evaluateScript.handler(
|
|
{params: {function: String(() => 2 * 5)}},
|
|
response,
|
|
context,
|
|
);
|
|
const lineEvaluation = response.responseLines.at(2)!;
|
|
assert.strictEqual(JSON.parse(lineEvaluation), 10);
|
|
});
|
|
});
|
|
it('runs in selected page', async () => {
|
|
await withMcpContext(async (response, context) => {
|
|
await evaluateScript.handler(
|
|
{params: {function: String(() => document.title)}},
|
|
response,
|
|
context,
|
|
);
|
|
|
|
let lineEvaluation = response.responseLines.at(2)!;
|
|
assert.strictEqual(JSON.parse(lineEvaluation), '');
|
|
|
|
const page = await context.newPage();
|
|
await page.setContent(`
|
|
<head>
|
|
<title>New Page</title>
|
|
</head>
|
|
`);
|
|
|
|
response.resetResponseLineForTesting();
|
|
await evaluateScript.handler(
|
|
{params: {function: String(() => document.title)}},
|
|
response,
|
|
context,
|
|
);
|
|
|
|
lineEvaluation = response.responseLines.at(2)!;
|
|
assert.strictEqual(JSON.parse(lineEvaluation), 'New Page');
|
|
});
|
|
});
|
|
|
|
it('work for complex objects', async () => {
|
|
await withMcpContext(async (response, context) => {
|
|
const page = context.getSelectedPage();
|
|
|
|
await page.setContent(html`<script src="./scripts.js"></script> `);
|
|
|
|
await evaluateScript.handler(
|
|
{
|
|
params: {
|
|
function: String(() => {
|
|
const scripts = Array.from(
|
|
document.head.querySelectorAll('script'),
|
|
).map(s => ({src: s.src, async: s.async, defer: s.defer}));
|
|
|
|
return {scripts};
|
|
}),
|
|
},
|
|
},
|
|
response,
|
|
context,
|
|
);
|
|
const lineEvaluation = response.responseLines.at(2)!;
|
|
assert.deepEqual(JSON.parse(lineEvaluation), {
|
|
scripts: [],
|
|
});
|
|
});
|
|
});
|
|
|
|
it('work for async functions', async () => {
|
|
await withMcpContext(async (response, context) => {
|
|
const page = context.getSelectedPage();
|
|
|
|
await page.setContent(html`<script src="./scripts.js"></script> `);
|
|
|
|
await evaluateScript.handler(
|
|
{
|
|
params: {
|
|
function: String(async () => {
|
|
await new Promise(res => setTimeout(res, 0));
|
|
return 'Works';
|
|
}),
|
|
},
|
|
},
|
|
response,
|
|
context,
|
|
);
|
|
const lineEvaluation = response.responseLines.at(2)!;
|
|
assert.strictEqual(JSON.parse(lineEvaluation), 'Works');
|
|
});
|
|
});
|
|
|
|
it('work with one argument', async () => {
|
|
await withMcpContext(async (response, context) => {
|
|
const page = context.getSelectedPage();
|
|
|
|
await page.setContent(html`<button id="test">test</button>`);
|
|
|
|
await context.createTextSnapshot();
|
|
|
|
await evaluateScript.handler(
|
|
{
|
|
params: {
|
|
function: String(async (el: Element) => {
|
|
return el.id;
|
|
}),
|
|
args: [{uid: '1_1'}],
|
|
},
|
|
},
|
|
response,
|
|
context,
|
|
);
|
|
const lineEvaluation = response.responseLines.at(2)!;
|
|
assert.strictEqual(JSON.parse(lineEvaluation), 'test');
|
|
});
|
|
});
|
|
|
|
it('work with multiple args', async () => {
|
|
await withMcpContext(async (response, context) => {
|
|
const page = context.getSelectedPage();
|
|
|
|
await page.setContent(html`<button id="test">test</button>`);
|
|
|
|
await context.createTextSnapshot();
|
|
|
|
await evaluateScript.handler(
|
|
{
|
|
params: {
|
|
function: String((container: Element, child: Element) => {
|
|
return container.contains(child);
|
|
}),
|
|
args: [{uid: '1_0'}, {uid: '1_1'}],
|
|
},
|
|
},
|
|
response,
|
|
context,
|
|
);
|
|
const lineEvaluation = response.responseLines.at(2)!;
|
|
assert.strictEqual(JSON.parse(lineEvaluation), true);
|
|
});
|
|
});
|
|
|
|
it('work for elements inside iframes', async () => {
|
|
server.addHtmlRoute(
|
|
'/iframe',
|
|
html`<main><button>I am iframe button</button></main>`,
|
|
);
|
|
server.addHtmlRoute('/main', html`<iframe src="/iframe"></iframe>`);
|
|
|
|
await withMcpContext(async (response, context) => {
|
|
const page = context.getSelectedPage();
|
|
await page.goto(server.getRoute('/main'));
|
|
await context.createTextSnapshot();
|
|
await evaluateScript.handler(
|
|
{
|
|
params: {
|
|
function: String((element: Element) => {
|
|
return element.textContent;
|
|
}),
|
|
args: [{uid: '1_3'}],
|
|
},
|
|
},
|
|
response,
|
|
context,
|
|
);
|
|
const lineEvaluation = response.responseLines.at(2)!;
|
|
assert.strictEqual(JSON.parse(lineEvaluation), 'I am iframe button');
|
|
});
|
|
});
|
|
it('evaluates in service worker', async () => {
|
|
await withMcpContext(async (response, context) => {
|
|
const expectedResult = 'workerResult';
|
|
const mockWorker = {
|
|
evaluateHandle: async () => {
|
|
const fn = Object.assign(() => expectedResult, {
|
|
dispose: async () => {
|
|
// do nothing
|
|
},
|
|
});
|
|
return fn;
|
|
},
|
|
evaluate: async (
|
|
fn: (...args: unknown[]) => unknown,
|
|
...args: unknown[]
|
|
) => {
|
|
return fn(...args);
|
|
},
|
|
};
|
|
|
|
const workerTarget = {
|
|
worker: async () => mockWorker,
|
|
};
|
|
const serviceWorker = {
|
|
target: workerTarget,
|
|
};
|
|
|
|
const swStub = sinon
|
|
.stub(context, 'getExtensionServiceWorkers')
|
|
.returns([serviceWorker as unknown as ExtensionServiceWorker]);
|
|
|
|
const swIdStub = sinon
|
|
.stub(context, 'getExtensionServiceWorkerId')
|
|
.returns('sw-1');
|
|
|
|
await evaluateScript.handler(
|
|
{
|
|
params: {
|
|
function: `() => '${expectedResult}'`,
|
|
serviceWorkerId: 'sw-1',
|
|
},
|
|
},
|
|
response,
|
|
context,
|
|
);
|
|
|
|
const lineEvaluation = response.responseLines.at(2)!;
|
|
assert.strictEqual(JSON.parse(lineEvaluation), expectedResult);
|
|
|
|
swStub.restore();
|
|
swIdStub.restore();
|
|
});
|
|
});
|
|
it('runs on page when serviceWorkerId is provided but extensions are disabled', async () => {
|
|
await withMcpContext(async (response, context) => {
|
|
await evaluateScriptTool(false).handler(
|
|
{
|
|
params: {
|
|
function: '() => document.title',
|
|
serviceWorkerId: 'sw-1',
|
|
},
|
|
},
|
|
response,
|
|
context,
|
|
);
|
|
const lineEvaluation = response.responseLines.at(2)!;
|
|
assert.strictEqual(JSON.parse(lineEvaluation), '');
|
|
});
|
|
});
|
|
});
|
|
});
|