196 lines
6.2 KiB
TypeScript
196 lines
6.2 KiB
TypeScript
/**
|
|
* CMS Content Management E2E Tests
|
|
*
|
|
* Tests the CMS admin pages integrated into platform-admin:
|
|
* - Landing content drafts list
|
|
* - Marketplace content drafts list
|
|
* - Content editor (new draft creation)
|
|
* - Content calendar view
|
|
* - Navigation between CMS pages
|
|
*/
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
import { applyAllMocks } from './fixtures/api-mocks';
|
|
|
|
test.describe('CMS - Landing Content', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await applyAllMocks(page);
|
|
});
|
|
|
|
test('should load landing content drafts page', async ({ page }) => {
|
|
await page.goto('/landing/content');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Page should render the content drafts title
|
|
const heading = page.locator('h1');
|
|
await expect(heading).toContainText('Content Drafts');
|
|
});
|
|
|
|
test('should display draft status tabs', async ({ page }) => {
|
|
await page.goto('/landing/content');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Should have status filter tabs
|
|
const allTab = page.locator('button', { hasText: 'All' });
|
|
await expect(allTab).toBeVisible();
|
|
});
|
|
|
|
test('should navigate to new draft editor', async ({ page }) => {
|
|
await page.goto('/landing/content');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Click the "New Draft" button
|
|
const newDraftButton = page.locator('button', { hasText: /new draft/i });
|
|
if (await newDraftButton.isVisible()) {
|
|
await newDraftButton.click();
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Should navigate to the editor page
|
|
expect(page.url()).toContain('/landing/content/new');
|
|
}
|
|
});
|
|
|
|
test('should load content calendar page', async ({ page }) => {
|
|
await page.goto('/landing/content/calendar');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Calendar page should render with month title
|
|
const heading = page.locator('h1');
|
|
await expect(heading).toContainText('Content Calendar');
|
|
});
|
|
|
|
test('should navigate calendar months', async ({ page }) => {
|
|
await page.goto('/landing/content/calendar');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Get the current month label
|
|
const monthLabel = page.locator('h2');
|
|
const initialMonth = await monthLabel.textContent();
|
|
|
|
// Click next month
|
|
const nextButton = page.locator('button', { hasText: /next/i });
|
|
await nextButton.click();
|
|
|
|
// Month label should change
|
|
const newMonth = await monthLabel.textContent();
|
|
expect(newMonth).not.toBe(initialMonth);
|
|
});
|
|
});
|
|
|
|
test.describe('CMS - Marketplace Content', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await applyAllMocks(page);
|
|
});
|
|
|
|
test('should load marketplace content drafts page', async ({ page }) => {
|
|
await page.goto('/marketplace/content');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const heading = page.locator('h1');
|
|
await expect(heading).toContainText('Content Drafts');
|
|
});
|
|
|
|
test('should load marketplace content calendar', async ({ page }) => {
|
|
await page.goto('/marketplace/content/calendar');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const heading = page.locator('h1');
|
|
await expect(heading).toContainText('Content Calendar');
|
|
});
|
|
});
|
|
|
|
test.describe('CMS - Content Editor', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await applyAllMocks(page);
|
|
});
|
|
|
|
test('should render new draft editor with namespace picker', async ({ page }) => {
|
|
await page.goto('/landing/content/new');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Title should indicate new draft
|
|
const heading = page.locator('h1');
|
|
await expect(heading).toContainText('New Draft');
|
|
|
|
// Namespace select should be present
|
|
const namespaceSelect = page.locator('select#namespace-select');
|
|
await expect(namespaceSelect).toBeVisible();
|
|
});
|
|
|
|
test('should show back button that navigates to drafts list', async ({ page }) => {
|
|
await page.goto('/landing/content/new');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const backButton = page.locator('button', { hasText: /back/i });
|
|
await expect(backButton).toBeVisible();
|
|
|
|
await backButton.click();
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Should navigate back to content list
|
|
expect(page.url()).toContain('/landing/content');
|
|
expect(page.url()).not.toContain('/new');
|
|
});
|
|
|
|
test('should show save button', async ({ page }) => {
|
|
await page.goto('/landing/content/new');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const saveButton = page.locator('button', { hasText: /save/i });
|
|
await expect(saveButton).toBeVisible();
|
|
});
|
|
|
|
test('should render draft editor with side-by-side panels', async ({ page }) => {
|
|
await page.goto('/landing/content/new');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Editor area should contain the draft editor component
|
|
const editorArea = page.locator('textarea');
|
|
await expect(editorArea.first()).toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe('CMS - Route Health', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await applyAllMocks(page);
|
|
});
|
|
|
|
const cmsRoutes = [
|
|
{ path: '/landing/content', name: 'Landing Drafts' },
|
|
{ path: '/landing/content/calendar', name: 'Landing Calendar' },
|
|
{ path: '/landing/content/new', name: 'Landing New Draft' },
|
|
{ path: '/marketplace/content', name: 'Marketplace Drafts' },
|
|
{ path: '/marketplace/content/calendar', name: 'Marketplace Calendar' },
|
|
{ path: '/marketplace/content/new', name: 'Marketplace New Draft' },
|
|
];
|
|
|
|
for (const route of cmsRoutes) {
|
|
test(`${route.name} (${route.path}) should render without JS errors`, async ({ page }) => {
|
|
const errors: string[] = [];
|
|
|
|
page.on('pageerror', (error) => {
|
|
errors.push(error.message);
|
|
});
|
|
|
|
page.on('console', (msg) => {
|
|
if (msg.type() === 'error') {
|
|
errors.push(msg.text());
|
|
}
|
|
});
|
|
|
|
await page.goto(route.path);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Filter expected errors (WebSocket, API fetch failures in test env)
|
|
const unexpectedErrors = errors.filter(
|
|
(e) =>
|
|
!e.includes('WebSocket') &&
|
|
!e.includes('socket.io') &&
|
|
!e.includes('Failed to fetch') &&
|
|
!e.includes('net::ERR_'),
|
|
);
|
|
|
|
expect(unexpectedErrors).toEqual([]);
|
|
});
|
|
}
|
|
});
|