veza/tests/e2e/26-smoke.spec.ts
senke 463ad5386b test: update e2e test suite and add audit tests
Refine auth, player, tracks, playlists, search, workflows, edge cases,
forms, responsive, network errors, error boundary, performance, visual
regression, cross-browser, profile, smoke, storybook, chat, and session
tests. Add audit test suite (accessibility, ethical, functional, design
tokens). Update test helpers and visual snapshots.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:06:26 +01:00

203 lines
6.6 KiB
TypeScript

import { test, expect } from '@playwright/test';
import { loginViaAPI, CONFIG, navigateTo, fillForm } from './helpers';
/**
* Smoke Tests @smoke @critical
*
* Combined from smoke-post-deploy.spec.ts and smoke.spec.ts.
* Quick checks to verify the application is functional.
*/
test.describe('SMOKE TESTS @smoke @critical', () => {
test.describe('Post-deploy smoke checks', () => {
test('homepage loads', async ({ page }) => {
const response = await page.goto('/', {
waitUntil: 'domcontentloaded',
timeout: 15000,
});
expect(response?.status()).toBeLessThan(500);
});
test('login page loads', async ({ page }) => {
const response = await page.goto('/login', {
waitUntil: 'domcontentloaded',
timeout: 15000,
});
expect(response?.status()).toBeLessThan(500);
});
test('API health check', async ({ request }) => {
const baseURL = CONFIG.apiURL;
const apiUrl = `${baseURL}/api/v1/health`;
try {
const response = await request.get(apiUrl, { timeout: 10000 });
expect(response.status()).toBeLessThan(500);
} catch {
// API health endpoint may not be reachable from this context
}
});
});
test.describe('Critical User Flows', () => {
test('complete user journey: Login -> Dashboard -> Navigation', async ({
page,
}) => {
test.setTimeout(90000);
// Step 1: Login
await loginViaAPI(
page,
CONFIG.users.listener.email,
CONFIG.users.listener.password,
);
// Verify user is authenticated — after login, URL should not be /login
await page.waitForURL((url) => !url.pathname.includes('/login'), { timeout: 15000 }).catch(() => {});
expect(page.url()).not.toContain('/login');
await expect(
page.locator('nav[role="navigation"], aside[role="navigation"]'),
).toBeVisible({ timeout: 10000 });
const isAuthenticated = await page.evaluate(() => {
try {
const authStorage = localStorage.getItem('auth-storage');
if (authStorage) {
const parsed = JSON.parse(authStorage);
return parsed.state?.isAuthenticated === true;
}
} catch {
return false;
}
return false;
});
expect(isAuthenticated).toBe(true);
// Step 2: Navigate to playlists
await navigateTo(page, '/playlists');
await page.waitForTimeout(1000);
// Verify page loaded
const body = page.locator('body');
const bodyText = (await body.textContent()) || '';
expect(bodyText.length).toBeGreaterThan(50);
});
test('Login -> Create Playlist (no upload)', async ({ page }) => {
test.setTimeout(90000);
await loginViaAPI(
page,
CONFIG.users.listener.email,
CONFIG.users.listener.password,
);
await page.waitForURL((url) => !url.pathname.includes('/login'), { timeout: 15000 }).catch(() => {});
await page.waitForTimeout(1_000);
if (page.url().includes('/login')) {
console.log(' Login failed — skipping');
return;
}
// Navigate to playlists
await navigateTo(page, '/playlists');
// Try to find and click create button — scope to main content to avoid sidebar matches
const mainContent = page.locator('main').first();
const mainVisible = await mainContent.isVisible({ timeout: 5_000 }).catch(() => false);
const searchScope = mainVisible ? mainContent : page;
const createButton = searchScope
.locator(
'button:has-text("Create"), button:has-text("Créer"), button:has-text("Nouvelle"), button:has-text("New")',
)
.first();
const isCreateVisible = await createButton
.isVisible({ timeout: 10_000 })
.catch(() => false);
if (!isCreateVisible) {
console.log(' Create button not visible — skipping playlist creation');
} else {
await createButton.click({ force: true, timeout: 10_000 });
await page.waitForTimeout(500);
// Fill playlist form if modal appeared
const titleInput = page
.locator('input[id="title"], input[name="title"]')
.first();
const isTitleVisible = await titleInput
.isVisible({ timeout: 3000 })
.catch(() => false);
if (isTitleVisible) {
await titleInput.fill('Quick Test Playlist');
// Scope to the dialog to avoid clicking the sidebar button behind the modal overlay
const dialog = page.locator('[role="dialog"]').first();
const dialogVisible = await dialog.isVisible({ timeout: 3000 }).catch(() => false);
const submitScope = dialogVisible ? dialog : page;
const submitBtn = submitScope
.locator(
'button:has-text("Créer"), button:has-text("Create"), button[type="submit"]',
)
.first();
if (await submitBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await submitBtn.click();
await page.waitForTimeout(2000);
}
}
}
// Verify page is still functional
const body = page.locator('body');
await expect(body).toBeVisible();
});
test('Login -> Upload Track (no playlist)', async ({ page }) => {
test.setTimeout(120000);
await loginViaAPI(
page,
CONFIG.users.listener.email,
CONFIG.users.listener.password,
);
await page.waitForURL((url) => !url.pathname.includes('/login'), { timeout: 15000 }).catch(() => {});
await page.waitForTimeout(1_000);
if (page.url().includes('/login')) {
console.log(' Login failed — skipping');
return;
}
// Navigate to library
await navigateTo(page, '/library');
// Try to find upload button
const uploadButton = page
.locator(
'button:has-text("Upload"), button:has-text("Envoyer"), button:has-text("Importer")',
)
.first();
const isUploadVisible = await uploadButton
.isVisible({ timeout: 5000 })
.catch(() => false);
if (isUploadVisible) {
await uploadButton.click();
await page.waitForTimeout(500);
// Check for file input
const fileInputLocator = page.locator('input[type="file"][accept*="audio"]');
const fileInputCount = await fileInputLocator.count();
if (fileInputCount > 0) {
console.log('Upload modal opened with file input');
}
}
// Verify page is still functional
const body = page.locator('body');
await expect(body).toBeVisible();
});
});
});