import { test, expect } from '@playwright/test'; const TEST_SOURCE = 'Test News'; test.describe('Home page', () => { test('loads and shows sources', async ({ page }) => { await page.goto('/'); await expect(page).toHaveTitle('NewsArchiver'); await expect(page.locator('h1').filter({ hasText: 'News Archives' })).toBeVisible(); await expect(page.locator('.newspaper-list')).toBeVisible(); }); test('shows source count', async ({ page }) => { await page.goto('/'); const count = await page.locator('p:has-text("Total sources:")').textContent(); expect(count || '').toMatch(/Total sources: \d+/); }); test('navigates to a source', async ({ page }) => { await page.goto('/'); const firstLink = page.locator('.newspaper-item h2 a').first(); const sourceName = (await firstLink.textContent())?.trim() || ''; await firstLink.click(); await expect(page).toHaveURL(/\/source\//); await expect(page.locator('h1')).toContainText(sourceName); }); }); test.describe('Article listing', () => { test('shows articles for a source', async ({ page }) => { await page.goto(`/source/${TEST_SOURCE.toLowerCase()}`); await expect(page.locator('.article-list')).toBeVisible(); const articles = page.locator('.article-item'); await expect.poll(() => articles.count()).toBeGreaterThan(0); }); test('article links navigate to detail', async ({ page }) => { await page.goto(`/source/${TEST_SOURCE.toLowerCase()}`); const firstArticle = page.locator('.article-item h3 a').first(); const title = (await firstArticle.textContent())?.trim() || ''; await firstArticle.click(); await expect(page).toHaveURL(/\/article\/\d+/); await expect(page.locator('.article-view h1')).toContainText(title); }); test('shows article metadata', async ({ page }) => { await page.goto(`/source/${TEST_SOURCE.toLowerCase()}/article/1`); await expect(page.locator('.article-source')).toBeVisible(); await expect(page.locator('.article-meta')).toBeVisible(); }); test('back link returns to listing', async ({ page }) => { await page.goto(`/source/${TEST_SOURCE.toLowerCase()}/article/1`); await page.locator('.back-link').click(); await expect(page).toHaveURL(new RegExp(`/source/${TEST_SOURCE.toLowerCase()}`)); await expect(page.locator('.article-list')).toBeVisible(); }); }); test.describe('Pagination', () => { test('shows page info', async ({ page }) => { await page.goto(`/source/${TEST_SOURCE.toLowerCase()}`); await expect(page.locator('.pagination span')).toContainText('Page 1'); }); }); test.describe('Navigation', () => { test('header nav links work', async ({ page }) => { await page.goto('/'); await page.locator('nav a:has-text("Archives")').click(); await expect(page).toHaveURL(/\/$/); await page.locator('nav a:has-text("Status")').click(); await expect(page).toHaveURL(/\/status/); }); }); test.describe('Status page', () => { test('shows system status', async ({ page }) => { await page.goto('/status'); await expect(page.locator('h1')).toBeVisible(); }); test('shows total articles count', async ({ page }) => { await page.goto('/status'); await expect(page.locator('text=/Total Articles: \d+/')).toBeVisible(); }); }); test.describe('RSS feed', () => { test('serves RSS XML', async ({ request }) => { const resp = await request.get('/rss'); expect(resp.ok()).toBeTruthy(); expect(resp.headers()['content-type']).toContain('application/rss+xml'); const body = await resp.text(); expect(body).toContain(''); }); }); test.describe('Atom feed', () => { test('serves Atom XML', async ({ request }) => { const resp = await request.get('/atom'); expect(resp.ok()).toBeTruthy(); expect(resp.headers()['content-type']).toContain('application/atom+xml'); const body = await resp.text(); expect(body).toContain(' { test('toggles dark mode', async ({ page }) => { await page.goto('/'); const html = page.locator('html'); await page.locator('#theme-toggle').click(); await expect(html).toHaveAttribute('data-theme', 'dark'); await page.locator('#theme-toggle').click(); await expect(html).toHaveAttribute('data-theme', 'light'); }); test('persists theme in localStorage', async ({ page }) => { await page.goto('/'); await page.locator('#theme-toggle').click(); const theme = await page.evaluate(() => localStorage.getItem('theme')); expect(theme).toBe('dark'); }); });