|
7 | 7 | import assert from 'node:assert'; |
8 | 8 | import {describe, it} from 'node:test'; |
9 | 9 |
|
| 10 | +import type {ParsedArguments} from '../../src/bin/chrome-devtools-mcp-cli-options.js'; |
| 11 | +import type {McpPage} from '../../src/McpPage.js'; |
10 | 12 | import {listPages, navigatePage, selectPage} from '../../src/tools/pages.js'; |
11 | | -import {withMcpContext} from '../utils.js'; |
| 13 | +import {executeWebMcpTool} from '../../src/tools/webmcp.js'; |
| 14 | +import {html, withMcpContext} from '../utils.js'; |
12 | 15 |
|
13 | 16 | describe('webmcp', () => { |
14 | | - it('list webmcp tools in navigate_page response', async () => { |
15 | | - await withMcpContext(async (response, context) => { |
16 | | - await navigatePage.handler( |
17 | | - {params: {url: 'about:blank'}, page: context.getSelectedMcpPage()}, |
18 | | - response, |
19 | | - context, |
20 | | - ); |
21 | | - assert.ok(response.listWebMcpTools); |
| 17 | + describe('list_webmcp_tools', () => { |
| 18 | + it('list webmcp tools in navigate_page response', async () => { |
| 19 | + await withMcpContext(async (response, context) => { |
| 20 | + await navigatePage.handler( |
| 21 | + {params: {url: 'about:blank'}, page: context.getSelectedMcpPage()}, |
| 22 | + response, |
| 23 | + context, |
| 24 | + ); |
| 25 | + assert.ok(response.listWebMcpTools); |
| 26 | + }); |
| 27 | + }); |
| 28 | + |
| 29 | + it('list webmcp tools in list_pages response', async () => { |
| 30 | + await withMcpContext(async (response, context) => { |
| 31 | + await listPages().handler({params: {}}, response, context); |
| 32 | + assert.ok(response.listWebMcpTools); |
| 33 | + }); |
22 | 34 | }); |
23 | | - }); |
24 | 35 |
|
25 | | - it('list webmcp tools in list_pages response', async () => { |
26 | | - await withMcpContext(async (response, context) => { |
27 | | - await listPages().handler({params: {}}, response, context); |
28 | | - assert.ok(response.listWebMcpTools); |
| 36 | + it('list webmcp tools in select_page response', async () => { |
| 37 | + await withMcpContext(async (response, context) => { |
| 38 | + const pageId = |
| 39 | + context.getPageId(context.getSelectedMcpPage().pptrPage) ?? 1; |
| 40 | + await selectPage.handler({params: {pageId}}, response, context); |
| 41 | + assert.ok(response.listWebMcpTools); |
| 42 | + }); |
29 | 43 | }); |
30 | 44 | }); |
31 | 45 |
|
32 | | - it('list webmcp tools in select_page response', async () => { |
33 | | - await withMcpContext(async (response, context) => { |
34 | | - const pageId = |
35 | | - context.getPageId(context.getSelectedMcpPage().pptrPage) ?? 1; |
36 | | - await selectPage.handler({params: {pageId}}, response, context); |
37 | | - assert.ok(response.listWebMcpTools); |
| 46 | + describe('execute_webmcp_tool', () => { |
| 47 | + async function setupWebMcpTool(page: McpPage) { |
| 48 | + await page.pptrPage.setContent( |
| 49 | + html`<form |
| 50 | + toolname="test_tool" |
| 51 | + tooldescription="A test tool" |
| 52 | + toolautosubmit |
| 53 | + ></form |
| 54 | + ><script> |
| 55 | + document.querySelector('form').onsubmit = event => { |
| 56 | + event.preventDefault(); |
| 57 | + event.respondWith('hello'); |
| 58 | + }; |
| 59 | + </script>`, |
| 60 | + ); |
| 61 | + } |
| 62 | + |
| 63 | + // TODO: Remove `.skip` once Chrome 149 reaches stable channel. |
| 64 | + it.skip('executes a tool successfully', async () => { |
| 65 | + await withMcpContext( |
| 66 | + async (response, context) => { |
| 67 | + const page = context.getSelectedMcpPage(); |
| 68 | + await setupWebMcpTool(page); |
| 69 | + |
| 70 | + await executeWebMcpTool.handler( |
| 71 | + {params: {toolName: 'test_tool', input: JSON.stringify({})}, page}, |
| 72 | + response, |
| 73 | + context, |
| 74 | + ); |
| 75 | + assert.strictEqual( |
| 76 | + response.responseLines[0], |
| 77 | + JSON.stringify({status: 'Completed', output: 'hello'}, null, 2), |
| 78 | + ); |
| 79 | + }, |
| 80 | + {args: ['--enable-features=WebMCPTesting,DevToolsWebMCPSupport']}, |
| 81 | + {experimentalWebmcp: true} as ParsedArguments, |
| 82 | + ); |
| 83 | + }); |
| 84 | + |
| 85 | + it('throws if tool is not found', async () => { |
| 86 | + await withMcpContext( |
| 87 | + async (response, context) => { |
| 88 | + await assert.rejects( |
| 89 | + async () => { |
| 90 | + await executeWebMcpTool.handler( |
| 91 | + { |
| 92 | + params: {toolName: 'missing-tool', input: JSON.stringify({})}, |
| 93 | + page: context.getSelectedMcpPage(), |
| 94 | + }, |
| 95 | + response, |
| 96 | + context, |
| 97 | + ); |
| 98 | + }, |
| 99 | + {message: /Tool missing-tool not found/}, |
| 100 | + ); |
| 101 | + }, |
| 102 | + {args: ['--enable-features=WebMCPTesting,DevToolsWebMCPSupport']}, |
| 103 | + {experimentalWebmcp: true} as ParsedArguments, |
| 104 | + ); |
| 105 | + }); |
| 106 | + |
| 107 | + it('throws if input is invalid', async () => { |
| 108 | + await withMcpContext( |
| 109 | + async (response, context) => { |
| 110 | + await assert.rejects( |
| 111 | + async () => { |
| 112 | + const page = context.getSelectedMcpPage(); |
| 113 | + await setupWebMcpTool(page); |
| 114 | + |
| 115 | + await executeWebMcpTool.handler( |
| 116 | + {params: {toolName: 'test_tool', input: 'invalid'}, page}, |
| 117 | + response, |
| 118 | + context, |
| 119 | + ); |
| 120 | + }, |
| 121 | + { |
| 122 | + message: |
| 123 | + /Failed to parse input as JSON: Unexpected token 'i', "invalid" is not valid JSON/, |
| 124 | + }, |
| 125 | + ); |
| 126 | + }, |
| 127 | + {args: ['--enable-features=WebMCPTesting,DevToolsWebMCPSupport']}, |
| 128 | + {experimentalWebmcp: true} as ParsedArguments, |
| 129 | + ); |
38 | 130 | }); |
39 | 131 | }); |
40 | 132 | }); |
0 commit comments