chore(tests): 🔧 Update test file blog.spec.ts to reflect latest expected behavior

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Lilith 2026-02-14 01:33:12 -08:00
parent 5fa6a1f4d5
commit 60cb759f4e

View file

@ -0,0 +1,183 @@
/**
* Blog E2E Integration Tests
*
* Verifies blog functionality on atlilith.www:
* - Blog index page renders with posts
* - Individual blog post pages load
* - Blog API responds with correct data
* - Widget-based posts render their custom components
* - RSS feed is accessible
*/
import { test, expect } from '@playwright/test';
const BASE_URL = process.env.BASE_URL || 'http://www.atlilith.e2e.local';
const BLOG_API_URL = process.env.BLOG_API_URL || 'http://blog-api:3021';
test.describe('Blog Integration', () => {
test.describe('Blog API', () => {
test('GET /api/blog/posts returns published posts for atlilith.com', async ({
request,
}) => {
const response = await request.get(
`${BASE_URL}/api/blog/posts?domain=atlilith.com&status=published&limit=10`,
);
expect(response.ok()).toBeTruthy();
const body = await response.json();
expect(body).toHaveProperty('data');
expect(body).toHaveProperty('total');
expect(body).toHaveProperty('page');
expect(Array.isArray(body.data)).toBeTruthy();
expect(body.data.length).toBeGreaterThan(0);
// Verify post structure
const post = body.data[0];
expect(post).toHaveProperty('id');
expect(post).toHaveProperty('title');
expect(post).toHaveProperty('slug');
expect(post).toHaveProperty('excerpt');
expect(post).toHaveProperty('status', 'published');
});
test('GET /api/blog/posts/:slug returns a specific post', async ({
request,
}) => {
const response = await request.get(
`${BASE_URL}/api/blog/posts/privacy-audit-2026`,
);
expect(response.ok()).toBeTruthy();
const post = await response.json();
expect(post).toHaveProperty('slug', 'privacy-audit-2026');
expect(post).toHaveProperty('title');
expect(post).toHaveProperty('domain', 'atlilith.com');
expect(post).toHaveProperty('status', 'published');
});
test('GET /api/blog/series returns series data', async ({ request }) => {
const response = await request.get(
`${BASE_URL}/api/blog/series?domain=atlilith.com`,
);
expect(response.ok()).toBeTruthy();
const body = await response.json();
expect(Array.isArray(body.data || body)).toBeTruthy();
});
test('Blog API health check responds', async ({ request }) => {
// Direct API health check (internal network)
const response = await request.get(`${BLOG_API_URL}/health`);
expect(response.ok()).toBeTruthy();
});
});
test.describe('Blog Frontend Pages', () => {
test('Blog index page loads and displays posts', async ({ page }) => {
await page.goto('/blog');
// Wait for the page to load
await page.waitForLoadState('networkidle');
// The blog index should render the "Blog" heading
await expect(page.locator('h1')).toContainText('Blog');
// Should have post cards rendered (seeded data includes 13+ posts)
const postCards = page.locator('a[href*="/blog/"]');
await expect(postCards.first()).toBeVisible({ timeout: 15000 });
});
test('Blog post page loads with content', async ({ page }) => {
// Navigate to a seeded post (privacy audit comparison - widget post)
await page.goto('/blog/privacy-audit-2026');
await page.waitForLoadState('networkidle');
// The page should not show "Post not found" error
await expect(page.locator('text=Post not found')).not.toBeVisible();
// Should have rendered some content (either widget or standard post)
// The privacy-audit-2026 is a widget post, so it renders WidgetPageRenderer
await expect(page.locator('body')).not.toBeEmpty();
});
test('Blog series post page loads', async ({ page }) => {
// Navigate to a seeded series chapter post
await page.goto('/blog/privacy-audit-2026-full-report');
await page.waitForLoadState('networkidle');
// Should not show error
await expect(page.locator('text=Post not found')).not.toBeVisible();
});
test('Blog category page loads', async ({ page }) => {
// Navigate to category filter page
await page.goto('/blog/category/privacy');
await page.waitForLoadState('networkidle');
// Should render without crashing - even if no category exists yet,
// the page should gracefully handle it
await expect(page.locator('body')).not.toBeEmpty();
});
test('Blog tag page loads', async ({ page }) => {
await page.goto('/blog/tag/privacy');
await page.waitForLoadState('networkidle');
// Should render without crashing
await expect(page.locator('body')).not.toBeEmpty();
});
});
test.describe('Blog RSS Feed', () => {
test('RSS feed returns valid XML', async ({ request }) => {
const response = await request.get(
`${BASE_URL}/api/blog/feed/rss?domain=atlilith.com`,
);
// RSS feed should respond (may be 200 or 404 if not implemented yet)
if (response.ok()) {
const contentType = response.headers()['content-type'] || '';
expect(
contentType.includes('xml') || contentType.includes('rss'),
).toBeTruthy();
const body = await response.text();
expect(body).toContain('<?xml');
expect(body).toContain('<rss');
}
});
});
test.describe('Blog Navigation', () => {
test('Clicking a post card navigates to the post page', async ({
page,
}) => {
await page.goto('/blog');
await page.waitForLoadState('networkidle');
// Find the first post link and click it
const firstPostLink = page.locator('a[href*="/blog/"]').first();
await expect(firstPostLink).toBeVisible({ timeout: 15000 });
const href = await firstPostLink.getAttribute('href');
expect(href).toBeTruthy();
await firstPostLink.click();
await page.waitForLoadState('networkidle');
// Should have navigated to a blog post URL
expect(page.url()).toContain('/blog/');
// Should not show the blog index heading anymore (we're on a post page)
// or it could be a nested route - just verify no error
await expect(page.locator('text=Post not found')).not.toBeVisible();
});
});
});