Read and validate files (CSV, XLSX, PDF, ZIP) with automatic parsing, type-safe results, and download handling. Simplify file operations in Playwright tests with built-in format support and validation helpers.
Testing file operations in Playwright requires boilerplate:
The file-utils module provides:
Context: User clicks button, CSV downloads, validate contents.
Implementation:
import { handleDownload, readCSV } from '@seontechnologies/playwright-utils/file-utils';
import path from 'node:path';
const DOWNLOAD_DIR = path.join(__dirname, '../downloads');
test('should download and validate CSV', async ({ page }) => {
const downloadPath = await handleDownload({
page,
downloadDir: DOWNLOAD_DIR,
trigger: () => page.click('[data-testid="export-csv"]'),
});
const { content } = await readCSV({ filePath: downloadPath });
// Validate headers
expect(content.headers).toEqual(['ID', 'Name', 'Email', 'Role']);
// Validate data
expect(content.data).toHaveLength(10);
expect(content.data[0]).toMatchObject({
ID: expect.any(String),
Name: expect.any(String),
Email: expect.stringMatching(/@/),
});
});
Key Points:
handleDownload waits for download, returns file pathreadCSV auto-parses to { headers, data }afterEachContext: Excel file with multiple sheets (e.g., Summary, Details, Errors).
Implementation:
import { readXLSX } from '@seontechnologies/playwright-utils/file-utils';
test('should read multi-sheet XLSX', async () => {
const downloadPath = await handleDownload({
page,
downloadDir: DOWNLOAD_DIR,
trigger: () => page.click('[data-testid="export-xlsx"]'),
});
const { content } = await readXLSX({ filePath: downloadPath });
// Access specific sheets
const summarySheet = content.sheets.find((s) => s.name === 'Summary');
const detailsSheet = content.sheets.find((s) => s.name === 'Details');
// Validate summary
expect(summarySheet.data).toHaveLength(1);
expect(summarySheet.data[0].TotalRecords).toBe('150');
// Validate details
expect(detailsSheet.data).toHaveLength(150);
expect(detailsSheet.headers).toContain('TransactionID');
});
Key Points:
sheets array with name and data propertiesContext: Validate PDF report contains expected content.
Implementation:
import { readPDF } from '@seontechnologies/playwright-utils/file-utils';
test('should validate PDF report', async () => {
const downloadPath = await handleDownload({
page,
downloadDir: DOWNLOAD_DIR,
trigger: () => page.click('[data-testid="download-report"]'),
});
const { content } = await readPDF({ filePath: downloadPath });
// content.text is extracted text from all pages
expect(content.text).toContain('Financial Report Q4 2024');
expect(content.text).toContain('Total Revenue:');
// Validate page count
expect(content.numpages).toBeGreaterThan(10);
});
Key Points:
content.text contains all extracted textcontent.numpages for page countContext: Validate ZIP contains expected files and extract specific file.
Implementation:
import { readZIP } from '@seontechnologies/playwright-utils/file-utils';
test('should validate ZIP archive', async () => {
const downloadPath = await handleDownload({
page,
downloadDir: DOWNLOAD_DIR,
trigger: () => page.click('[data-testid="download-backup"]'),
});
const { content } = await readZIP({ filePath: downloadPath });
// Check file list
expect(content.files).toContain('data.csv');
expect(content.files).toContain('config.json');
expect(content.files).toContain('readme.txt');
// Read specific file from archive
const configContent = content.zip.readAsText('config.json');
const config = JSON.parse(configContent);
expect(config.version).toBe('2.0');
});
Key Points:
content.files lists all files in archivecontent.zip.readAsText() extracts specific filesContext: API endpoint returns file download (not UI click).
Implementation:
test('should download via API', async ({ page, request }) => {
const downloadPath = await handleDownload({
page,
downloadDir: DOWNLOAD_DIR,
trigger: async () => {
const response = await request.get('/api/export/csv', {
headers: { Authorization: 'Bearer token' },
});
if (!response.ok()) {
throw new Error(`Export failed: ${response.status()}`);
}
},
});
const { content } = await readCSV({ filePath: downloadPath });
expect(content.data).toHaveLength(100);
});
Key Points:
trigger can be async API callContent-Disposition headerpage for download events// CSV validation
const { isValid, errors } = await validateCSV({
filePath: downloadPath,
expectedRowCount: 10,
requiredHeaders: ['ID', 'Name', 'Email'],
});
expect(isValid).toBe(true);
expect(errors).toHaveLength(0);
test.afterEach(async () => {
// Clean up downloaded files
await fs.remove(DOWNLOAD_DIR);
});
overview.md - Installation and importsapi-request.md - API-triggered downloadsrecurse.md - Poll for file generation completion❌ Not cleaning up downloads:
test('creates file', async () => {
await handleDownload({ ... })
// File left in downloads folder
})
✅ Clean up after tests:
test.afterEach(async () => {
await fs.remove(DOWNLOAD_DIR);
});