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

  1. /upload — upload a file via setInputFiles, assert the success message.
  2. /download — capture a download and verify its filename.
  3. /drag_and_drop — swap boxes A and B; try dragTo first, fall back to manual mouse.
  4. /javascript_alerts — accept a confirm dialog and assert the result text.

Interview questions