import { test, expectFixture as expect } from '../helpers/test-fixtures'; const demoTenantCredentials = { email: process.env.E2E_DEMO_TENANT_EMAIL ?? 'tenant-demo@fotospiel.app', password: process.env.E2E_DEMO_TENANT_PASSWORD ?? 'Demo1234!', }; test.describe('Checkout Payment Step – Lemon Squeezy flow', () => { test('opens Lemon Squeezy checkout and shows success notice', async ({ page }) => { await page.route('**/lemonsqueezy/create-checkout', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ checkout_url: 'https://fotospiel.lemonsqueezy.com/checkout/success', }), }); }); await page.route('https://app.lemonsqueezy.com/js/lemon.js', async (route) => { await route.fulfill({ status: 200, contentType: 'application/javascript', body: ` window.createLemonSqueezy = function() {}; window.LemonSqueezy = { Setup: function(options) { window.__lemonEventHandler = options?.eventHandler || null; }, Url: { Open: function(url) { window.__lemonOpenedUrl = url; } } }; `, }); }); await openCheckoutPaymentStep(page, demoTenantCredentials); await acceptCheckoutTerms(page); await page.evaluate(() => { window.__openedUrls = []; window.open = (url: string, target?: string | null, features?: string | null) => { window.__openedUrls.push({ url, target: target ?? null, features: features ?? null }); return null; }; }); await page.getByRole('button', { name: /Continue with Lemon Squeezy|Weiter mit Lemon Squeezy/ }).first().click(); await expect( page.locator( 'text=/secure overlay|Overlay|neuen Tab|new tab/i' ) ).toBeVisible(); let mode: 'inline' | 'hosted' | null = null; for (let i = 0; i < 8; i++) { const state = await page.evaluate(() => ({ inline: Boolean(window.__lemonOpenedUrl), opened: window.__openedUrls?.length ?? 0, })); if (state.inline) { mode = 'inline'; break; } if (state.opened > 0) { mode = 'hosted'; break; } await page.waitForTimeout(300); } expect(mode).not.toBeNull(); if (mode === 'inline') { const inlineUrl = await page.evaluate(() => window.__lemonOpenedUrl ?? null); expect(inlineUrl).not.toBeNull(); } if (mode === 'hosted') { await expect.poll(async () => { return page.evaluate(() => window.__openedUrls?.[0]?.url ?? null); }).toContain('lemonsqueezy'); } }); test('shows error state when Lemon Squeezy checkout creation fails', async ({ page }) => { await page.route('**/lemonsqueezy/create-checkout', async (route) => { await route.fulfill({ status: 500, contentType: 'application/json', body: JSON.stringify({ message: 'test-error' }), }); }); await page.route('https://app.lemonsqueezy.com/js/lemon.js', async (route) => { await route.fulfill({ status: 200, contentType: 'application/javascript', body: ` window.createLemonSqueezy = function() {}; window.LemonSqueezy = { Setup: function(options) { window.__lemonEventHandler = options?.eventHandler || null; }, Url: { Open: function() { throw new Error('forced Lemon Squeezy failure'); } } }; `, }); }); await openCheckoutPaymentStep(page, demoTenantCredentials); await acceptCheckoutTerms(page); await page.getByRole('button', { name: /Continue with Lemon Squeezy|Weiter mit Lemon Squeezy/ }).first().click(); await expect( page.locator('text=/Lemon Squeezy-Checkout konnte nicht gestartet werden|Lemon Squeezy checkout could not be started/i') ).toBeVisible(); }); }); async function openCheckoutPaymentStep( page: import('@playwright/test').Page, credentials: { email: string; password: string } ) { await page.goto('/packages'); const detailsButtons = page.getByRole('button', { name: /Details ansehen|Details anzeigen|View details/i, }); await expect(detailsButtons.first()).toBeVisible(); await detailsButtons.first().click(); const dialog = page.getByRole('dialog'); await expect(dialog).toBeVisible(); await dialog.getByRole('link', { name: /Jetzt bestellen|Order now|Jetzt buchen/i }).click(); await expect(page).toHaveURL(/\/(bestellen|checkout)\/\d+/); await page.getByRole('button', { name: /^Weiter$/ }).first().click(); const continueButton = page.getByRole('button', { name: /Weiter zur Zahlung|Continue to Payment/i }); if (await continueButton.isVisible()) { await continueButton.click(); } else { await page.getByRole('button', { name: /^Anmelden$/ }).first().click(); await expect(page.locator('input[name="identifier"]')).toBeVisible(); await page.fill('input[name="identifier"]', credentials.email); await page.fill('input[name="password"]', credentials.password); await page.getByRole('button', { name: /^Anmelden$/ }).last().click(); } await expect(page.getByPlaceholder(/Gutscheincode/i)).toBeVisible(); } async function acceptCheckoutTerms(page: import('@playwright/test').Page) { const termsCheckbox = page.locator('#checkout-terms-hero'); await expect(termsCheckbox).toBeVisible(); await termsCheckbox.click(); } declare global { interface Window { __openedUrls?: Array<{ url: string; target?: string | null; features?: string | null }>; __lemonOpenedUrl?: string | null; __lemonEventHandler?: ((event: { event: string; data?: unknown }) => void) | null; } }