Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions tests/tools/input.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import fs from 'node:fs/promises';
import path from 'node:path';
import {describe, it} from 'node:test';

import sinon from 'sinon';

import type {ParsedArguments} from '../../src/bin/chrome-devtools-mcp-cli-options.js';
import {McpResponse} from '../../src/McpResponse.js';
import {TextSnapshot} from '../../src/TextSnapshot.js';
Expand Down Expand Up @@ -1294,6 +1296,71 @@ describe('input', () => {
await fs.unlink(testFilePath);
});
});

it('does not probe a chooser-opening element with uploadFile', async () => {
const testFilePath = path.join(process.cwd(), 'test.txt');
await fs.writeFile(testFilePath, 'test file content');

await withMcpContext(async (response, context) => {
const page = context.getSelectedPptrPage();
await page.setContent(
html`<button id="file-chooser-button">Upload file</button>
<input
type="file"
id="file-input"
style="display: none;"
/>
<script>
document
.getElementById('file-chooser-button')
.addEventListener('click', () => {
document.getElementById('file-input').click();
});
</script>`,
);
const mcpPage = context.getSelectedMcpPage();
mcpPage.textSnapshot = await TextSnapshot.create(mcpPage);

// Real headful Chrome doesn't throw when uploadFile is called on a
// chooser-opening element — it kills the renderer, and the file is
// never set. The in-process test browser instead throws, which lets
// the old code recover via the chooser and masks the bug. Stub
// uploadFile to a silent no-op so the button behaves like it does in
// production: if the tool probes it with uploadFile, the file is lost.
const buttonHandle = await mcpPage.getElementByUid('1_1');
const uploadFileStub = sinon
.stub(buttonHandle, 'uploadFile')
.resolves();
sinon.stub(mcpPage, 'getElementByUid').resolves(buttonHandle);

try {
await uploadFile.handler(
{
params: {
uid: '1_1',
filePath: testFilePath,
},
page: mcpPage,
},
response,
context,
);

// The element isn't a file input, so it must never be probed with
// uploadFile — it has to go straight to the chooser...
assert.strictEqual(uploadFileStub.called, false);
// ...and the chooser path must actually deliver the file.
const uploadedFileName = await page.$eval('#file-input', el => {
return (el as HTMLInputElement).files?.[0]?.name;
});
assert.strictEqual(uploadedFileName, 'test.txt');
} finally {
sinon.restore();
}

await fs.unlink(testFilePath);
});
});
});

describe('press_key', () => {
Expand Down