chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import assert from 'node:assert';
|
||||
import {afterEach, describe, it} from 'node:test';
|
||||
|
||||
import sinon from 'sinon';
|
||||
|
||||
import {parseArguments} from '../src/bin/chrome-devtools-mcp-cli-options.js';
|
||||
import {McpContext} from '../src/McpContext.js';
|
||||
import {McpPage} from '../src/McpPage.js';
|
||||
import {Mutex} from '../src/Mutex.js';
|
||||
import {zod} from '../src/third_party/index.js';
|
||||
import {ToolHandler} from '../src/ToolHandler.js';
|
||||
import {ToolCategory} from '../src/tools/categories.js';
|
||||
import type {
|
||||
DefinedPageTool,
|
||||
ToolDefinition,
|
||||
} from '../src/tools/ToolDefinition.js';
|
||||
|
||||
describe('ToolHandler', () => {
|
||||
afterEach(() => {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
it('calls page getter for page scoped tools', async () => {
|
||||
let handlerCalled = false;
|
||||
const tool: DefinedPageTool = {
|
||||
name: 'page_tool',
|
||||
description: 'A page scoped tool',
|
||||
annotations: {
|
||||
category: ToolCategory.INPUT,
|
||||
readOnlyHint: false,
|
||||
},
|
||||
schema: {},
|
||||
blockedByDialog: false,
|
||||
verifyFilesSchema: [],
|
||||
pageScoped: true,
|
||||
handler: async () => {
|
||||
handlerCalled = true;
|
||||
},
|
||||
};
|
||||
|
||||
const mockContext = sinon.createStubInstance(McpContext);
|
||||
const mockPage = sinon.createStubInstance(McpPage);
|
||||
mockContext.getSelectedMcpPage.returns(mockPage);
|
||||
mockContext.detectOpenDevToolsWindows.resolves();
|
||||
|
||||
const toolMutex = new Mutex();
|
||||
const serverArgs = parseArguments('1.0.0', ['node', 'script.js'], {
|
||||
CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: 'true',
|
||||
});
|
||||
|
||||
const toolHandler = new ToolHandler(
|
||||
tool,
|
||||
serverArgs,
|
||||
async () => mockContext,
|
||||
toolMutex,
|
||||
);
|
||||
|
||||
assert.strictEqual(toolHandler.shouldRegister, true);
|
||||
await toolHandler.handle({});
|
||||
|
||||
assert.strictEqual(mockContext.getSelectedMcpPage.calledOnce, true);
|
||||
assert.strictEqual(handlerCalled, true);
|
||||
});
|
||||
|
||||
it('does not call page getter for non-page scoped tools', async () => {
|
||||
let handlerCalled = false;
|
||||
const tool: ToolDefinition = {
|
||||
name: 'global_tool',
|
||||
description: 'A global tool',
|
||||
annotations: {
|
||||
category: ToolCategory.NAVIGATION,
|
||||
readOnlyHint: true,
|
||||
},
|
||||
schema: {},
|
||||
blockedByDialog: false,
|
||||
verifyFilesSchema: [],
|
||||
handler: async () => {
|
||||
handlerCalled = true;
|
||||
},
|
||||
};
|
||||
|
||||
const mockContext = sinon.createStubInstance(McpContext);
|
||||
mockContext.detectOpenDevToolsWindows.resolves();
|
||||
|
||||
const toolMutex = new Mutex();
|
||||
const serverArgs = parseArguments('1.0.0', ['node', 'script.js'], {
|
||||
CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: 'true',
|
||||
});
|
||||
|
||||
const toolHandler = new ToolHandler(
|
||||
tool,
|
||||
serverArgs,
|
||||
async () => mockContext,
|
||||
toolMutex,
|
||||
);
|
||||
|
||||
assert.strictEqual(toolHandler.shouldRegister, true);
|
||||
const result = await toolHandler.handle({});
|
||||
|
||||
assert.strictEqual(mockContext.getSelectedMcpPage.called, false);
|
||||
assert.strictEqual(mockContext.getPageById.called, false);
|
||||
assert.strictEqual(handlerCalled, true);
|
||||
assert.strictEqual(result.isError, undefined);
|
||||
});
|
||||
|
||||
it('reports unknown registered tool arguments clearly', async () => {
|
||||
let handlerCalled = false;
|
||||
const tool: ToolDefinition = {
|
||||
name: 'lenient_tool',
|
||||
description: 'A tool with a required argument',
|
||||
annotations: {
|
||||
category: ToolCategory.NAVIGATION,
|
||||
readOnlyHint: true,
|
||||
},
|
||||
schema: {
|
||||
url: zod.string(),
|
||||
},
|
||||
blockedByDialog: false,
|
||||
verifyFilesSchema: [],
|
||||
handler: async () => {
|
||||
handlerCalled = true;
|
||||
},
|
||||
};
|
||||
|
||||
const mockContext = sinon.createStubInstance(McpContext);
|
||||
mockContext.detectOpenDevToolsWindows.resolves();
|
||||
|
||||
const toolMutex = new Mutex();
|
||||
const serverArgs = parseArguments('1.0.0', ['node', 'script.js'], {
|
||||
CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: 'true',
|
||||
});
|
||||
|
||||
const toolHandler = new ToolHandler(
|
||||
tool,
|
||||
serverArgs,
|
||||
async () => mockContext,
|
||||
toolMutex,
|
||||
);
|
||||
|
||||
const params = {url: 'https://example.com', description: 'open the page'};
|
||||
assert.strictEqual(
|
||||
toolHandler.registeredInputSchema.safeParse(params).success,
|
||||
true,
|
||||
);
|
||||
|
||||
const result = await toolHandler.handle(params);
|
||||
|
||||
assert.strictEqual(result.isError, true);
|
||||
assert.match(
|
||||
result.content[0].type === 'text' ? result.content[0].text : '',
|
||||
/Unknown argument for tool "lenient_tool": "description"\. Expected arguments: "url"\./,
|
||||
);
|
||||
assert.strictEqual(handlerCalled, false);
|
||||
});
|
||||
|
||||
it('sets shouldRegister to false and returns disabled reason when category is disabled', async () => {
|
||||
let handlerCalled = false;
|
||||
const tool: ToolDefinition = {
|
||||
name: 'disabled_tool',
|
||||
description: 'A disabled tool',
|
||||
annotations: {
|
||||
category: ToolCategory.EMULATION,
|
||||
readOnlyHint: true,
|
||||
},
|
||||
schema: {},
|
||||
blockedByDialog: false,
|
||||
verifyFilesSchema: [],
|
||||
handler: async () => {
|
||||
handlerCalled = true;
|
||||
},
|
||||
};
|
||||
|
||||
const mockContext = sinon.createStubInstance(McpContext);
|
||||
const toolMutex = new Mutex();
|
||||
const serverArgs = parseArguments(
|
||||
'1.0.0',
|
||||
['node', 'script.js', '--categoryEmulation=false'],
|
||||
{CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: 'true'},
|
||||
);
|
||||
|
||||
const toolHandler = new ToolHandler(
|
||||
tool,
|
||||
serverArgs,
|
||||
async () => mockContext,
|
||||
toolMutex,
|
||||
);
|
||||
|
||||
assert.strictEqual(toolHandler.shouldRegister, false);
|
||||
|
||||
const result = await toolHandler.handle({});
|
||||
assert.strictEqual(result.isError, true);
|
||||
assert.match(
|
||||
result.content[0].type === 'text' ? result.content[0].text : '',
|
||||
/is currently disabled/,
|
||||
);
|
||||
assert.strictEqual(handlerCalled, false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user