Skip to content
Merged
Show file tree
Hide file tree
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
9 changes: 7 additions & 2 deletions packages/injected/src/recorder/recorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,10 +412,15 @@ class RecordActionTool implements RecorderTool {
const target = this._recorder.deepEventTarget(event);

if (target.nodeName === 'INPUT' && (target as HTMLInputElement).type.toLowerCase() === 'file') {
// When the file input is hidden and triggered by another element (e.g. a button with
// onclick="input.click()"), the hover model points to the trigger, not the input.
// Derive the selector from the actual target element in that case.
const selector = target === this._hoveredElement
? this._hoveredModel!.selector
: this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName }).selector;
this._recordAction({
name: 'setInputFiles',
// webkit doesn't focus file inputs on click, so activeModel is unreliable here.
selector: this._hoveredModel!.selector,
selector,
signals: [],
files: [...((target as HTMLInputElement).files || [])].map(file => file.name),
});
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/tools/mcp/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export function decorateMCPCommand(command: Command) {
sharedBrowserPromise = undefined;
const browserContext = (backend as BrowserBackend).browserContext;
await browserContext.close().catch(() => { });
await browserContext.browser()!.close().catch(() => { });
await browserContext.browser()?.close().catch(() => { });
}
};
await mcpServer.start(factory, config.server);
Expand Down
22 changes: 22 additions & 0 deletions tests/library/inspector/cli-codegen-2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,28 @@ await page.GetByRole(AriaRole.Button, new() { Name = "Choose File" }).SetInputFi
await page.GetByRole(AriaRole.Button, new() { Name = "Choose File" }).SetInputFilesAsync(new[] { });`);
});

test('should upload a file via hidden input triggered by button', async ({ openRecorder, browserName, asset, isLinux }) => {
const { page, recorder } = await openRecorder();
await recorder.setContentAndWait(`
<button onclick="document.querySelector('input[type=file]').click()">select file</button>
<input type="file" style="display: none">
`);

await recorder.hoverOverElement('button');
const [chooser] = await Promise.all([
page.waitForEvent('filechooser'),
recorder.trustedClick(),
]);
await new Promise(f => setTimeout(f, 1000));
await chooser.setFiles(asset('file-to-upload.txt'));

const sources = await recorder.waitForOutput('JavaScript', 'setInputFiles');

// The setInputFiles call must target the actual file input, not the trigger button.
expect(sources.get('JavaScript')!.text).toContain(`setInputFiles('file-to-upload.txt')`);
expect(sources.get('JavaScript')!.text).not.toContain(`getByRole('button', { name: 'select file' }).setInputFiles`);
});

test('should download files', async ({ openRecorder, server }) => {
const { page, recorder } = await openRecorder();

Expand Down
Loading