level 03 / advanced-interactions
Advanced Interactions
File upload and download, drag and drop, geolocation, permissions, and browser dialogs — the interactions beyond click and fill.
practice site: The Internet
File upload
// Direct: set files on the <input type="file">
await page.getByLabel('Upload CV').setInputFiles('fixtures/cv.pdf');
// Multiple files
await page.getByLabel('Photos').setInputFiles([
'fixtures/photo1.jpg',
'fixtures/photo2.jpg',
]);
// From memory — no file on disk needed
await page.getByLabel('Upload').setInputFiles({
name: 'report.csv',
mimeType: 'text/csv',
buffer: Buffer.from('id,name\n1,Alice'),
});
// Clear selection
await page.getByLabel('Upload').setInputFiles([]);
When the input is hidden behind a styled button that opens the OS dialog:
// The OS file dialog cannot be automated — intercept the chooser instead
const [chooser] = await Promise.all([
page.waitForEvent('filechooser'),
page.getByRole('button', { name: 'Browse files' }).click(),
]);
await chooser.setFiles('fixtures/cv.pdf');
File download
test('export downloads a CSV', async ({ page }) => {
const [download] = await Promise.all([
page.waitForEvent('download'),
page.getByRole('button', { name: 'Export CSV' }).click(),
]);
expect(download.suggestedFilename()).toBe('orders.csv');
// Save somewhere stable and verify content
const path = await download.path();
const content = await fs.promises.readFile(path!, 'utf-8');
expect(content).toContain('order_id');
});
Key insight: The Promise.all listener-before-trigger pattern appears again — downloads, popups, dialogs, file choosers, and network waits all use it. Learn it once, apply it everywhere.
Drag and drop
// High-level — works for HTML5 drag events
await page.getByText('Column A').dragTo(page.getByText('Column B'));
// Manual mouse control — for pointer-event libraries (Sortable.js, dnd-kit)
const source = page.getByTestId('card-1');
const target = page.getByTestId('done-column');
const from = await source.boundingBox();
const to = await target.boundingBox();
await page.mouse.move(from!.x + from!.width / 2, from!.y + from!.height / 2);
await page.mouse.down();
await page.mouse.move(to!.x + to!.width / 2, to!.y + to!.height / 2, { steps: 10 });
await page.mouse.up();
steps: 10 emits intermediate mousemove events — many drag libraries ignore
a single instant jump.
Geolocation
test.use({
geolocation: { latitude: 51.5074, longitude: -0.1278 }, // London
permissions: ['geolocation'],
});
test('map centres on my location', async ({ page }) => {
await page.goto('https://www.openstreetmap.org');
await page.getByRole('button', { name: 'Show My Location' }).click();
await expect(page).toHaveURL(/map=.*51\.50/);
});
// Change location mid-test (simulate movement)
await context.setGeolocation({ latitude: 48.8566, longitude: 2.3522 }); // Paris
Permissions
// Grant upfront via context options or test.use
test.use({ permissions: ['clipboard-read', 'notifications'] });
// Grant later, scoped to an origin
await context.grantPermissions(['camera'], { origin: 'https://meet.example.com' });
// Revoke everything
await context.clearPermissions();
Without a grant, the browser would show a permission prompt — which Playwright cannot click, because it’s browser UI, not page content. Grant permissions instead of trying to handle prompts.
Dialogs: alert, confirm, prompt
// Dialogs are auto-DISMISSED by default. Register a handler to accept:
page.on('dialog', async dialog => {
expect(dialog.type()).toBe('confirm');
expect(dialog.message()).toContain('Delete this item?');
await dialog.accept();
});
await page.getByRole('button', { name: 'Delete' }).click();
Reference
locator.setInputFiles(path)
Upload file(s) to input[type=file]
page.waitForEvent('filechooser')
Intercept OS file dialog
page.waitForEvent('download')
Capture a triggered download
download.path()
Temp path of downloaded file
locator.dragTo(target)
HTML5 drag and drop
page.mouse.move/down/up
Manual drag for pointer-event libraries
context.setGeolocation(coords)
Set or change GPS position
context.grantPermissions([...])
Grant browser permissions (no prompt)
page.on('dialog', handler)
Handle alert/confirm/prompt
Practice on The Internet
/upload— upload a file viasetInputFiles, assert the success message./download— capture a download and verify its filename./drag_and_drop— swap boxes A and B; trydragTofirst, fall back to manual mouse./javascript_alerts— accept a confirm dialog and assert the result text.