diff --git a/tests/tools/input.test.ts b/tests/tools/input.test.ts
index 29c81e513..b7935f154 100644
--- a/tests/tools/input.test.ts
+++ b/tests/tools/input.test.ts
@@ -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';
@@ -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`
+
+ `,
+ );
+ 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', () => {