rework of the e2e test suites
This commit is contained in:
160
tests/ui/admin/event-admin-dashboard.test.ts
Normal file
160
tests/ui/admin/event-admin-dashboard.test.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import type { Page } from '@playwright/test';
|
||||
|
||||
import { test, expectFixture as expect } from '../helpers/test-fixtures';
|
||||
|
||||
const futureDate = (daysAhead = 10): string => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() + daysAhead);
|
||||
return date.toISOString().slice(0, 10);
|
||||
};
|
||||
|
||||
async function ensureOnDashboard(page: Page): Promise<void> {
|
||||
await page.goto('/event-admin/dashboard');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
if (page.url().includes('/event-admin/welcome')) {
|
||||
const directButton = page.getByRole('button', { name: /Direkt zum Dashboard/i });
|
||||
if (await directButton.isVisible()) {
|
||||
await directButton.click();
|
||||
await page.waitForURL(/\/event-admin\/dashboard$/, { timeout: 15_000 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test.describe('Tenant Admin PWA – end-to-end coverage', () => {
|
||||
test.beforeEach(async ({ signInTenantAdmin }) => {
|
||||
await signInTenantAdmin();
|
||||
});
|
||||
|
||||
test('dashboard highlights core stats and quick actions', async ({ page }) => {
|
||||
await ensureOnDashboard(page);
|
||||
|
||||
await expect(page.getByRole('heading', { name: /Hallo/i })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: /Neues Event/i })).toBeVisible();
|
||||
await expect(page.getByText(/Schnellaktionen/i)).toBeVisible();
|
||||
await expect(page.getByText(/Kommende Events/i)).toBeVisible();
|
||||
});
|
||||
|
||||
test('event creation flow and detail subsections', async ({ page }) => {
|
||||
const eventName = `Playwright Event ${Date.now()}`;
|
||||
const eventDate = futureDate(14);
|
||||
|
||||
await page.goto('/event-admin/events/new');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await expect(page.getByRole('heading', { name: /Eventdetails/i })).toBeVisible();
|
||||
|
||||
await page.getByLabel(/Eventname/i).fill(eventName);
|
||||
await page.getByLabel(/Datum/i).fill(eventDate);
|
||||
|
||||
const eventTypeTrigger = page.getByRole('combobox', { name: /Event-Typ/i });
|
||||
await eventTypeTrigger.click();
|
||||
const firstOption = page.getByRole('option').first();
|
||||
await expect(firstOption).toBeVisible({ timeout: 5_000 });
|
||||
await firstOption.click();
|
||||
|
||||
await page.getByRole('button', { name: /^Speichern/i }).click();
|
||||
await expect(page).toHaveURL(/\/event-admin\/events\/[a-z0-9-]+$/, { timeout: 20_000 });
|
||||
const createdSlug = page.url().split('/').pop() ?? '';
|
||||
|
||||
await expect(page.getByRole('heading', { name: /Eventdetails/i })).toBeVisible();
|
||||
|
||||
await page.goto('/event-admin/events');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await expect(page.getByText(eventName, { exact: false })).toBeVisible();
|
||||
|
||||
await page.goto(`/event-admin/events/${createdSlug}/photos`);
|
||||
await expect(page.getByRole('heading', { name: /Fotos moderieren/i })).toBeVisible();
|
||||
await expect(page.getByText(/Noch keine Fotos vorhanden/i)).toBeVisible();
|
||||
|
||||
await page.goto(`/event-admin/events/${createdSlug}/members`);
|
||||
await expect(page.getByRole('heading', { name: /Event-Mitglieder/i })).toBeVisible();
|
||||
|
||||
await page.goto(`/event-admin/events/${createdSlug}/tasks`);
|
||||
await expect(page.getByRole('heading', { name: /Event-Tasks/i })).toBeVisible();
|
||||
await expect(page.getByText(/Noch keine Tasks zugewiesen/i)).toBeVisible();
|
||||
});
|
||||
|
||||
test('task library allows creating custom tasks', async ({ page }) => {
|
||||
await page.goto('/event-admin/tasks');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await expect(page.getByRole('heading', { name: /Task Bibliothek/i })).toBeVisible();
|
||||
|
||||
const taskTitle = `Playwright Task ${Date.now()}`;
|
||||
await page.getByRole('button', { name: /^Neu$/i }).click();
|
||||
await page.getByLabel(/Titel/i).fill(taskTitle);
|
||||
await page.getByLabel(/Beschreibung/i).fill('Automatisierter Testfall');
|
||||
await page.getByRole('button', { name: /^Speichern$/i }).click();
|
||||
|
||||
await expect(page.getByText(taskTitle)).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
await page.goto('/event-admin/task-collections');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await expect(page.getByRole('heading', { name: /Aufgabenvorlagen/i })).toBeVisible();
|
||||
});
|
||||
|
||||
test('supporting sections (emotions, billing, settings) load successfully', async ({ page }) => {
|
||||
await page.goto('/event-admin/emotions');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await expect(page.getByRole('heading', { name: /Emotionen/i })).toBeVisible();
|
||||
|
||||
await page.goto('/event-admin/billing');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await expect(page.getByRole('heading', { name: /Pakete & Abrechnung/i })).toBeVisible();
|
||||
|
||||
await page.goto('/event-admin/settings');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await expect(page.getByRole('heading', { name: /Einstellungen/i })).toBeVisible();
|
||||
});
|
||||
|
||||
test('wedding event workflow assigns tasks and exposes join token', async ({ page, fetchJoinToken }) => {
|
||||
const eventName = `Playwright Hochzeit ${Date.now()}`;
|
||||
const eventDate = futureDate(21);
|
||||
|
||||
await page.goto('/event-admin/events/new');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await page.getByLabel(/Eventname/i).fill(eventName);
|
||||
await page.getByLabel(/Datum/i).fill(eventDate);
|
||||
|
||||
const eventTypeCombo = page.getByRole('combobox', { name: /Event-Typ/i });
|
||||
await eventTypeCombo.click();
|
||||
const weddingOption = page.getByRole('option', { name: /Hochzeit|Wedding/i }).first();
|
||||
await expect(weddingOption).toBeVisible();
|
||||
await weddingOption.click();
|
||||
|
||||
await page.getByRole('button', { name: /^Speichern/i }).click();
|
||||
await page.waitForURL(/\/event-admin\/events\/[a-z0-9-]+$/i, { timeout: 20_000 });
|
||||
const createdSlug = page.url().split('/').pop() ?? '';
|
||||
|
||||
await expect(page.getByText(/Hochzeit|Wedding/i)).toBeVisible();
|
||||
|
||||
await page.goto(`/event-admin/events/${createdSlug}/tasks`);
|
||||
await expect(page.getByRole('heading', { name: /Event-Tasks/i })).toBeVisible();
|
||||
|
||||
const librarySection = page
|
||||
.locator('section')
|
||||
.filter({ hasText: /Tasks aus Bibliothek hinzufügen|Add tasks/i })
|
||||
.first();
|
||||
const availableTaskLabels = librarySection.locator('label');
|
||||
const availableCount = await availableTaskLabels.count();
|
||||
test.skip(availableCount === 0, 'No task library entries available to assign');
|
||||
|
||||
const firstLabel = availableTaskLabels.first();
|
||||
const taskTitle = ((await firstLabel.locator('p').first().textContent()) ?? '').trim();
|
||||
await firstLabel.click();
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: /Ausgewählte Tasks zuweisen|Assign selected tasks/i })
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
page.locator('section').filter({ hasText: /Zugeordnete Tasks|Assigned tasks/i }).getByText(taskTitle, { exact: false }),
|
||||
).toBeVisible({ timeout: 15_000 });
|
||||
|
||||
const joinToken = await fetchJoinToken({ slug: createdSlug });
|
||||
expect(joinToken.token).toBeTruthy();
|
||||
expect(joinToken.join_url).toContain(joinToken.token);
|
||||
expect(joinToken.qr_svg).toContain('<svg');
|
||||
});
|
||||
});
|
||||
67
tests/ui/admin/tenant-onboarding-flow.test.ts
Normal file
67
tests/ui/admin/tenant-onboarding-flow.test.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { test, expectFixture as expect } from '../helpers/test-fixtures';
|
||||
|
||||
/**
|
||||
* Skeleton E2E coverage for the tenant onboarding journey.
|
||||
*
|
||||
* This suite is currently skipped until we have stable seed data and
|
||||
* authentication helpers for Playwright. Once those are in place we can
|
||||
* remove the skip and let the flow exercise the welcome -> packages -> summary
|
||||
* steps with mocked Stripe/Paddle APIs.
|
||||
*/
|
||||
test.describe('Tenant Onboarding Welcome Flow', () => {
|
||||
test('redirects unauthenticated users to login', async ({ page }) => {
|
||||
await page.goto('/event-admin/welcome');
|
||||
await expect(page).toHaveURL(/\/event-admin\/login/);
|
||||
await expect(page.getByText('Bitte warten', { exact: false })).toBeVisible();
|
||||
});
|
||||
|
||||
test('tenant admin can progress through welcome, packages, summary, and setup', async ({
|
||||
tenantAdminCredentials,
|
||||
signInTenantAdmin,
|
||||
page,
|
||||
}) => {
|
||||
test.skip(
|
||||
!tenantAdminCredentials,
|
||||
'Provide E2E_TENANT_EMAIL and E2E_TENANT_PASSWORD (see docs/testing/e2e.md) to run onboarding tests.'
|
||||
);
|
||||
|
||||
await signInTenantAdmin();
|
||||
|
||||
// If guard redirects to dashboard, hop to welcome manually.
|
||||
if (!page.url().includes('/event-admin/welcome')) {
|
||||
await page.goto('/event-admin/welcome');
|
||||
}
|
||||
|
||||
await expect(page.getByRole('heading', { name: /Willkommen im Event-Erlebnisstudio/i })).toBeVisible();
|
||||
|
||||
// Open package selection via CTA.
|
||||
await page.getByRole('button', { name: /Pakete entdecken/i }).click();
|
||||
await expect(page).toHaveURL(/\/event-admin\/welcome\/packages/);
|
||||
await expect(page.getByRole('heading', { name: /Wähle dein Eventpaket/i })).toBeVisible();
|
||||
|
||||
// Choose the first available package and ensure we land on the summary step.
|
||||
const choosePackageButton = page.getByRole('button', { name: /Paket wählen/i }).first();
|
||||
await choosePackageButton.waitFor({ state: 'visible', timeout: 10_000 });
|
||||
await choosePackageButton.click();
|
||||
|
||||
await expect(page).toHaveURL(/\/event-admin\/welcome\/summary/);
|
||||
await expect(page.getByRole('heading', { name: /Bestellübersicht/i })).toBeVisible();
|
||||
|
||||
// Validate payment sections. Depending on env we either see Stripe/Paddle widgets or configuration warnings.
|
||||
const stripeConfigured = Boolean(process.env.VITE_STRIPE_PUBLISHABLE_KEY);
|
||||
if (stripeConfigured) {
|
||||
await expect(page.getByRole('heading', { name: /Kartenzahlung \(Stripe\)/i })).toBeVisible();
|
||||
} else {
|
||||
await expect(
|
||||
page.getByText(/Stripe nicht verfügbar|PaymentIntent konnte nicht erstellt werden|Publishable Key fehlt/i)
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
await expect(page.getByRole('heading', { name: /^Paddle$/i })).toBeVisible();
|
||||
|
||||
// Continue to the setup step without completing a purchase.
|
||||
await page.getByRole('button', { name: /Weiter zum Setup/i }).click();
|
||||
await expect(page).toHaveURL(/\/event-admin\/welcome\/event/);
|
||||
await expect(page.getByRole('heading', { name: /Bereite dein erstes Event vor/i })).toBeVisible();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user