148 lines
6.4 KiB
TypeScript
148 lines
6.4 KiB
TypeScript
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/mobile/dashboard');
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
if (page.url().includes('/event-admin/mobile/welcome')) {
|
||
const directButton = page.getByRole('button', { name: /Direkt zum Dashboard|Jump to dashboard|Dashboard/i });
|
||
if (await directButton.isVisible()) {
|
||
await directButton.click();
|
||
await page.waitForURL(/\/event-admin\/mobile\/dashboard$/, { timeout: 15_000 });
|
||
}
|
||
}
|
||
}
|
||
|
||
test.describe('Tenant Admin PWA – end-to-end coverage', () => {
|
||
test.beforeEach(async ({ tenantAdminCredentials, signInTenantAdmin }) => {
|
||
test.skip(!tenantAdminCredentials, 'Provide E2E_TENANT_EMAIL and E2E_TENANT_PASSWORD to run admin tests.');
|
||
await signInTenantAdmin();
|
||
});
|
||
|
||
test('dashboard highlights core stats and quick actions', async ({ page }) => {
|
||
await ensureOnDashboard(page);
|
||
|
||
await expect(page.getByText(/Dashboard/i)).toBeVisible();
|
||
await expect(page.getByRole('button', { name: /Event erstellen|Create event/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/mobile/events/new');
|
||
await page.waitForLoadState('networkidle');
|
||
await expect(page.getByText(/Event/i)).toBeVisible();
|
||
|
||
await page.getByPlaceholder(/Sommerfest|Summer Party/i).fill(eventName);
|
||
await page.locator('input[type="datetime-local"]').fill(`${eventDate}T18:00`);
|
||
|
||
const typeSelect = page.locator('select').first();
|
||
await typeSelect.selectOption({ index: 1 });
|
||
|
||
await page.getByRole('button', { name: /Event erstellen|Create event/i }).click();
|
||
await expect(page).toHaveURL(/\/event-admin\/mobile\/events\/[a-z0-9-]+$/, { timeout: 20_000 });
|
||
|
||
const createdSlug = new URL(page.url()).pathname.split('/').pop() ?? '';
|
||
await expect(page.getByText(eventName)).toBeVisible();
|
||
|
||
await page.goto('/event-admin/mobile/events');
|
||
await page.waitForLoadState('networkidle');
|
||
await expect(page.getByText(eventName, { exact: false })).toBeVisible();
|
||
|
||
await page.goto(`/event-admin/mobile/events/${createdSlug}/photos`);
|
||
await expect(page.getByText(/Foto-Moderation|Photo moderation/i)).toBeVisible();
|
||
|
||
await page.goto(`/event-admin/mobile/events/${createdSlug}/members`);
|
||
await expect(page.getByText(/Event-Mitglieder|Event members/i)).toBeVisible();
|
||
|
||
await page.goto(`/event-admin/mobile/events/${createdSlug}/tasks`);
|
||
await expect(page.getByText(/Aufgaben & Missionen|Tasks & Checklists/i)).toBeVisible();
|
||
});
|
||
|
||
test('task library allows creating custom tasks', async ({ page }) => {
|
||
await page.goto('/event-admin/mobile/tasks');
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
await expect(page.getByText(/Tasks|Aufgaben/i)).toBeVisible();
|
||
|
||
const taskTitle = `Playwright Task ${Date.now()}`;
|
||
const addTaskButton = page.getByRole('button', { name: /Aufgabe hinzufügen|Add task/i }).first();
|
||
test.skip((await addTaskButton.count()) === 0, 'No active event available to add tasks.');
|
||
await addTaskButton.click();
|
||
await page.getByPlaceholder(/Gruppenfoto|Group photo/i).fill(taskTitle);
|
||
await page.getByPlaceholder(/Optionale Hinweise|Optional/i).fill('Automatisierter Testfall');
|
||
await page.getByRole('button', { name: /Aufgabe speichern|Save task/i }).click();
|
||
|
||
await expect(page.getByText(taskTitle)).toBeVisible({ timeout: 10_000 });
|
||
});
|
||
|
||
test('supporting sections (billing, settings, profile) load successfully', async ({ page }) => {
|
||
await page.goto('/event-admin/mobile/billing');
|
||
await page.waitForLoadState('networkidle');
|
||
await expect(page.getByText(/Pakete|Billing|Abrechnung/i)).toBeVisible();
|
||
|
||
await page.goto('/event-admin/mobile/settings');
|
||
await page.waitForLoadState('networkidle');
|
||
await expect(page.getByText(/Einstellungen|Settings/i)).toBeVisible();
|
||
|
||
await page.goto('/event-admin/mobile/profile');
|
||
await page.waitForLoadState('networkidle');
|
||
await expect(page.getByText(/Profil|Profile/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/mobile/events/new');
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
await page.getByPlaceholder(/Sommerfest|Summer Party/i).fill(eventName);
|
||
await page.locator('input[type="datetime-local"]').fill(`${eventDate}T12:00`);
|
||
|
||
const eventTypeCombo = page.locator('select').first();
|
||
await eventTypeCombo.selectOption({ index: 1 });
|
||
|
||
await page.getByRole('button', { name: /Event erstellen|Create event/i }).click();
|
||
await page.waitForURL(/\/event-admin\/mobile\/events\/[a-z0-9-]+$/i, { timeout: 20_000 });
|
||
const createdSlug = new URL(page.url()).pathname.split('/').pop() ?? '';
|
||
|
||
await page.goto(`/event-admin/mobile/events/${createdSlug}/tasks`);
|
||
await expect(page.getByText(/Aufgaben & Missionen|Tasks & Checklists/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');
|
||
});
|
||
});
|