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:
parent
5fa6a1f4d5
commit
60cb759f4e
1 changed files with 183 additions and 0 deletions
183
e2e/prod-auth/tests/blog.spec.ts
Normal file
183
e2e/prod-auth/tests/blog.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue