ci(web): Phase P6 — vite build in PR CI + headless E2E gate on trunk publishes (#1445)
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
- ci.yml: build step (vite build via turbo) runs on every PR pipeline after test - publish.yml: e2e step boots the gateway from built dist (HOME/cwd-isolated throwaway PGlite) and runs the Playwright suite headless inside mcr.microsoft.com/playwright:v1.58.2-noble against the SPA bundle served exactly as production serves it; both kaniko publishes now gate on e2e - serve-spa.ts: strip query strings before asset resolution; immutable cache-control for hashed assets (onSend hook); e2e spec covers both - e2e suite hardened: globalSetup seeds admin+member through real bootstrap/better-auth APIs (Origin header for CSRF), loginAs waits for the post-login redirect (fixes 27-skipped race), stale #152-era assertions rewritten to the current command-driven UI, strict-mode violations fixed with level-1 heading queries and .or() auto-retrying locators - verify-release mirrors the new build stage; playwright artifacts ignored
This commit is contained in:
@@ -8,9 +8,12 @@ test.describe('Sidebar navigation', () => {
|
||||
test.skip(!url.includes('/chat'), 'No seeded test user — skipping authenticated tests');
|
||||
});
|
||||
|
||||
test('sidebar shows Mosaic brand link', async ({ page }) => {
|
||||
test('sidebar shows the Mosaic brand', async ({ page }) => {
|
||||
await page.goto('/chat');
|
||||
await expect(page.getByRole('link', { name: /mosaic/i }).first()).toBeVisible();
|
||||
// The brand block is a logo image plus "Mosaic / Mission Control" text,
|
||||
// not a link.
|
||||
await expect(page.getByRole('img', { name: /mosaic logo/i })).toBeVisible();
|
||||
await expect(page.getByText('Mission Control')).toBeVisible();
|
||||
});
|
||||
|
||||
test('Chat nav link navigates to /chat', async ({ page }) => {
|
||||
@@ -48,11 +51,12 @@ test.describe('Sidebar navigation', () => {
|
||||
|
||||
test('active link is visually highlighted', async ({ page }) => {
|
||||
await page.goto('/chat');
|
||||
// The active link should have a distinct class — check that the Chat link
|
||||
// has the active style class (bg-blue-600/20 text-blue-400)
|
||||
// The sidebar marks the active item with `font-medium` (plus an inline
|
||||
// primary-color style); inactive items get the hover class instead.
|
||||
const chatLink = page.getByRole('link', { name: /^chat$/i }).first();
|
||||
const cls = await chatLink.getAttribute('class');
|
||||
expect(cls).toContain('blue');
|
||||
const projectsLink = page.getByRole('link', { name: /^projects$/i }).first();
|
||||
await expect(chatLink).toHaveClass(/font-medium/);
|
||||
await expect(projectsLink).not.toHaveClass(/font-medium/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -67,11 +71,13 @@ test.describe('Route transitions', () => {
|
||||
await page.goto('/chat');
|
||||
await expect(page).toHaveURL(/\/chat/);
|
||||
|
||||
// level: 1 — empty-state h2s ("No projects yet") also match the loose
|
||||
// patterns, and a two-element match is a strict-mode violation.
|
||||
await page.goto('/projects');
|
||||
await expect(page.getByRole('heading', { name: /projects/i })).toBeVisible();
|
||||
await expect(page.getByRole('heading', { level: 1, name: /projects/i })).toBeVisible();
|
||||
|
||||
await page.goto('/settings');
|
||||
await expect(page.getByRole('heading', { name: /settings/i })).toBeVisible();
|
||||
await expect(page.getByRole('heading', { level: 1, name: /settings/i })).toBeVisible();
|
||||
|
||||
await page.goto('/chat');
|
||||
await expect(page).toHaveURL(/\/chat/);
|
||||
|
||||
Reference in New Issue
Block a user