diff --git a/e2e/ownership.spec.ts b/e2e/ownership.spec.ts new file mode 100644 index 0000000..aaeb7cd --- /dev/null +++ b/e2e/ownership.spec.ts @@ -0,0 +1,81 @@ +import { test, expect } from '@playwright/test'; + +async function fetchVerifyLink(email: string): Promise { + for (let i = 0; i < 30; i++) { + const res = await fetch('http://localhost:8025/api/v1/messages?limit=30'); + const data = await res.json() as { messages: { ID: string; To: { Address: string }[] }[] }; + const msg = data.messages.find((m) => m.To.some((t) => t.Address === email)); + if (msg) { + const body = await (await fetch(`http://localhost:8025/api/v1/message/${msg.ID}`)).json() as { Text: string }; + const m = body.Text.match(/https?:\/\/[^\s]+verify-email\?token=[^\s]+/); + if (m) return m[0]; + } + await new Promise((r) => setTimeout(r, 250)); + } + throw new Error('no verify link for ' + email); +} + +async function registerAndVerify(page: import('@playwright/test').Page, name: string, email: string, password: string) { + await page.goto('/register'); + await page.getByLabel(/Naam/).fill(name); + await page.getByLabel(/E-mailadres/).fill(email); + await page.getByLabel(/Wachtwoord/).fill(password); + await page.getByRole('button', { name: /Account aanmaken/ }).click(); + await expect(page.getByText(/bevestigingsmail/i)).toBeVisible({ timeout: 10_000 }); + const link = await fetchVerifyLink(email); + await page.goto(link); + await expect(page.getByRole('link', { name: 'Naar inloggen' })).toBeVisible({ timeout: 10_000 }); +} + +async function loginAs(page: import('@playwright/test').Page, email: string, password: string) { + await page.goto('/login'); + await page.getByLabel(/E-mailadres/).fill(email); + await page.getByLabel(/Wachtwoord/).fill(password); + await page.getByRole('button', { name: 'Inloggen' }).click(); + await expect(page.getByRole('button', { name: 'Account menu' })).toBeVisible({ timeout: 15_000 }); +} + +async function logout(page: import('@playwright/test').Page) { + await page.getByRole('button', { name: 'Account menu' }).click(); + await page.getByRole('button', { name: 'Uitloggen' }).click(); + await expect(page).toHaveURL(/\/login/); +} + +test('user A shares lesson, B subscribes from marketplace and practices', async ({ page }) => { + const aEmail = `alice+${Date.now()}@example.com`; + const bEmail = `bob+${Date.now()}@example.com`; + const pw = 'secretpass'; + + // A registers + creates + shares lesson + adds card + await registerAndVerify(page, 'Alice', aEmail, pw); + await loginAs(page, aEmail, pw); + await page.goto('/admin'); + await page.getByPlaceholder(/Nieuwe wortel-les/).fill('Spaans-test'); + await page.getByRole('button', { name: /Toevoegen/ }).first().click(); + await page.getByRole('link', { name: /Spaans-test/ }).first().click(); + await page.getByPlaceholder('Nieuwe vraag').fill('hola'); + await page.getByPlaceholder('Antwoord').fill('hello'); + await page.getByRole('button', { name: 'Kaart toevoegen' }).click(); + await page.getByRole('button', { name: /Deel publiek/ }).click(); + await expect(page.getByRole('button', { name: /Maak privé/ })).toBeVisible(); + await logout(page); + + // B registers, finds in marketplace, subscribes, practices + await registerAndVerify(page, 'Bob', bEmail, pw); + await loginAs(page, bEmail, pw); + await page.goto('/marketplace'); + await expect(page.getByRole('heading', { name: 'Spaans-test' })).toBeVisible({ timeout: 10_000 }); + await page.getByRole('button', { name: 'Abonneer' }).first().click(); + + // Now visible in admin tree as subscribed + await page.goto('/admin'); + await expect(page.getByText(/📥 Geabonneerd/).first()).toBeVisible(); + + // Practice it + await page.getByRole('link', { name: /Spaans-test/ }).first().click(); + await page.getByRole('link', { name: /Start oefenen/ }).click(); + await page.getByRole('button', { name: /Start sessie/ }).click(); + await page.getByRole('button', { name: 'Toon antwoord' }).click(); + await page.getByRole('button', { name: /Goed/ }).click(); + await expect(page.getByText(/Sessie klaar/)).toBeVisible({ timeout: 8_000 }); +});