VALIDATION LAB•FILE UPLOAD
Date & Files
File Upload
Upload an image, verify size + type validation, and reject files that exceed the budget.
Scenario
Uploads are the security frontier — a single wrong MIME-check lets an .exe slip into the asset CDN. QA must verify both client AND server-side validation, never just the visible error.
Live widget · interact freely
Manual test checklist
- 1Upload a PNG under the size cap — preview thumbnail renders
- 2Upload a renamed `.exe` (or any unsupported MIME) — error appears
- 3Upload a >1MB file — size error appears
- 4Upload nothing then submit — graceful fallback, no crash
- 5Inspect the network request — is the file `multipart/form-data`?
- 6Drag-and-drop onto the input — same flow as click-to-pick
Expected result
A 50KB PNG renders a preview thumbnail; a 5MB upload triggers the size error.
Automation challenge
Use `setInputFiles({ name, mimeType, buffer })` to construct a synthetic 5 MB buffer in memory — no fixture file needed. Assert the size error fires. Then upload a real fixture PNG and assert the preview renders.
Stable selectors
- File input
[data-testid="upload-input"] - Preview thumbnail
[data-testid="upload-preview"] - Size error
[data-testid="upload-error"]
Locator strategy
Three levels from simple IDs to scoped Playwright locators. IDs and names are easy to learn but are not always the best long-term choice when labels change or components repeat.
Use simple IDs and names to understand how locating elements works.
123
await page.goto('https://lab.hakdogan.com/practice/file-upload');
await page.locator('#upload-input').setInputFiles('fixture.png');Avoid as primary strategies
XPath (unless there is no alternative), long CSS chains, Tailwind-style utility class selectors, generated or unstable IDs, and volatile framework internals break when layout, styling, or DOM structure shifts.
12
await page.locator('.w-full.rounded-xl.border.bg-blue-500').fill('demo@example.com');
await page.locator('//div[2]/form/div[1]/input').fill('demo@example.com');Reference Playwright spec
12345678910111213
import { test, expect } from '@playwright/test';
test('file upload rejects oversize file', async ({ page }) => {
await page.goto('https://lab.hakdogan.com/practice/file-upload');
await page.getByTestId('upload-input').setInputFiles({
name: 'huge.png',
mimeType: 'image/png',
buffer: Buffer.alloc(2 * 1024 * 1024),
});
await expect(page.getByTestId('upload-error')).toContainText(/exceeds/i);
});Headed Test Playback
Simulated headed-browser flow — no real browser is launched.
Automation-style playback (Playwright-shaped logs). No real browser; no commands run on your machine.
Mixed: fill() for stable fields, type() / press() where keyboard semantics matter.
file-upload.spec.ts- Open upload widget
- Select avatar.png
- Validate file type
- Validate file size
- Show preview
- Assert upload success