// Playwright E2E tests for Advanced Search UI
const { test, expect } = require('@playwright/test');
async function loginAndSetTokens(page) {
// Read pre-generated access token
const fs = require('fs');
const path = require('path');
const tokenPath = path.resolve(__dirname, '..', '.e2e-token');
const access = fs.readFileSync(tokenPath, 'utf-8').trim();
const refresh = '';
await page.addInitScript((a, r) => {
try { window.localStorage.setItem('auth_token', a); } catch (_) {}
try { if (r) window.localStorage.setItem('refresh_token', r); } catch (_) {}
}, access, refresh);
return access;
}
async function apiCreateCustomer(page, payload, token) {
// Use import endpoint to avoid multiple writes and simplify schema
const req = await page.request.post('/api/import/customers', {
data: { customers: [payload] },
headers: token ? { Authorization: `Bearer ${token}` } : {},
});
expect(req.ok()).toBeTruthy();
// Return id directly
return payload.id;
}
async function apiCreateFile(page, payload, token) {
const req = await page.request.post('/api/import/files', {
data: { files: [payload] },
headers: token ? { Authorization: `Bearer ${token}` } : {},
});
expect(req.ok()).toBeTruthy();
return payload.file_no;
}
test.describe('Advanced Search UI', () => {
test.beforeEach(async ({ page }) => {
// no-op here; call per test to capture token
});
test('returns highlighted results and enforces XSS safety', async ({ page }) => {
const token = `E2E-${Date.now()}`;
const accessToken = await loginAndSetTokens(page);
const malicious = `${token}
`;
await apiCreateCustomer(page, {
id: `E2E-CUST-${Date.now()}`,
first: 'Alice',
last: malicious,
email: `alice.${Date.now()}@example.com`,
city: 'Austin',
abrev: 'TX',
}, accessToken);
await page.goto('/search');
await page.fill('#searchQuery', token);
await page.click('#advancedSearchForm button[type="submit"]');
await page.waitForResponse(res => res.url().includes('/api/search/advanced') && res.request().method() === 'POST');
const results = page.locator('#searchResults .search-result-item');
await expect(results.first()).toBeVisible({ timeout: 10000 });
const matchHtml = page.locator('#searchResults .search-result-item .text-sm.text-info-600');
if (await matchHtml.count()) {
const html = await matchHtml.first().innerHTML();
expect(html).toContain('');
expect(html).not.toContain('onerror');
expect(html).not.toContain('