Files
fotospiel-app/tests/e2e/checkout-payment.test.ts
Codex Agent a949c8d3af - Wired the checkout wizard for Google “comfort login”: added Socialite controller + dependency, new Google env
hooks in config/services.php/.env.example, and updated wizard steps/controllers to store session payloads,
attach packages, and surface localized success/error states.
- Retooled payment handling for both Stripe and PayPal, adding richer status management in CheckoutController/
PayPalController, fallback flows in the wizard’s PaymentStep.tsx, and fresh feature tests for intent
creation, webhooks, and the wizard CTA.
- Introduced a consent-aware Matomo analytics stack: new consent context, cookie-banner UI, useAnalytics/
useCtaExperiment hooks, and MatomoTracker component, then instrumented marketing pages (Home, Packages,
Checkout) with localized copy and experiment tracking.
- Polished package presentation across marketing UIs by centralizing formatting in PresentsPackages, surfacing
localized description tables/placeholders, tuning badges/layouts, and syncing guest/marketing translations.
- Expanded docs & reference material (docs/prp/*, TODOs, public gallery overview) and added a Playwright smoke
test for the hero CTA while reconciling outstanding checklist items.
2025-10-19 11:41:03 +02:00

192 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, expect } from '@playwright/test';
import { execSync } from 'child_process';
const LOGIN_EMAIL = 'checkout-e2e@example.com';
const LOGIN_PASSWORD = 'Password123!';
test.describe('Checkout Payment Step Stripe & PayPal states', () => {
test.beforeAll(async () => {
execSync(
`php artisan tenant:add-dummy --email=${LOGIN_EMAIL} --password=${LOGIN_PASSWORD} --first_name=Checkout --last_name=Tester --address="Playwrightstr. 1" --phone="+4912345678"`
);
execSync(
`php artisan tinker --execute="App\\\\Models\\\\User::where('email', '${LOGIN_EMAIL}')->update(['email_verified_at' => now()]);"`
);
});
test.beforeEach(async ({ page }) => {
await page.goto('/login');
await page.fill('input[name="email"]', LOGIN_EMAIL);
await page.fill('input[name="password"]', LOGIN_PASSWORD);
await page.getByRole('button', { name: /Anmelden|Login/ }).click();
await expect(page).toHaveURL(/dashboard/);
});
test('Stripe payment intent error surfaces descriptive status', async ({ page }) => {
await page.route('**/stripe/create-payment-intent', async (route) => {
await route.fulfill({
status: 422,
contentType: 'application/json',
body: JSON.stringify({ error: 'Test payment intent failure' }),
});
});
await openCheckoutPaymentStep(page);
await expect(
page.locator('text=/Fehler beim Laden der Zahlungsdaten|Error loading payment data/')
).toBeVisible();
await expect(
page.locator('text=/Zahlungsformular bereit|Payment form ready/')
).not.toBeVisible();
});
test('Stripe payment intent ready state renders when backend responds', async ({ page }) => {
await page.route('**/stripe/create-payment-intent', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ client_secret: 'pi_test_secret' }),
});
});
await openCheckoutPaymentStep(page);
await expect(
page.locator('text=/Zahlungsformular bereit\\. Bitte gib deine Daten ein\\.|Payment form ready\\./')
).toBeVisible();
});
test('PayPal approval success updates status', async ({ page }) => {
await stubPayPalSdk(page);
await page.route('**/paypal/create-order', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ id: 'ORDER_TEST', status: 'CREATED' }),
});
});
await page.route('**/paypal/capture-order', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ status: 'captured' }),
});
});
await openCheckoutPaymentStep(page);
await selectPayPalMethod(page);
await page.waitForFunction(() => window.__paypalButtonsConfig !== undefined);
await page.evaluate(async () => {
const config = window.__paypalButtonsConfig;
if (!config) return;
await config.createOrder();
await config.onApprove({ orderID: 'ORDER_TEST' });
});
await expect(
page.locator('text=/Zahlung bestätigt\\. Bestellung wird abgeschlossen|Payment confirmed/')
).toBeVisible();
});
test('PayPal capture failure notifies user', async ({ page }) => {
await stubPayPalSdk(page);
await page.route('**/paypal/create-order', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ id: 'ORDER_FAIL', status: 'CREATED' }),
});
});
await page.route('**/paypal/capture-order', async (route) => {
await route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'capture_failed' }),
});
});
await openCheckoutPaymentStep(page);
await selectPayPalMethod(page);
await page.waitForFunction(() => window.__paypalButtonsConfig !== undefined);
await page.evaluate(async () => {
const config = window.__paypalButtonsConfig;
if (!config) return;
await config.createOrder();
await config.onApprove({ orderID: 'ORDER_FAIL' });
});
await expect(
page.locator('text=/PayPal capture failed|PayPal-Abbuchung fehlgeschlagen|PayPal capture error/')
).toBeVisible();
});
});
async function openCheckoutPaymentStep(page: import('@playwright/test').Page) {
await page.goto('/packages');
const checkoutLink = page.locator('a[href^="/checkout/"]').first();
const href = await checkoutLink.getAttribute('href');
if (!href) {
throw new Error('No checkout link found on packages page.');
}
await page.goto(href);
const nextButton = page.getByRole('button', {
name: /Weiter zum Zahlungsschritt|Continue to Payment/,
});
if (await nextButton.isVisible()) {
await nextButton.click();
}
await page.waitForSelector('text=/Zahlung|Payment/');
}
async function selectPayPalMethod(page: import('@playwright/test').Page) {
const paypalButton = page.getByRole('button', { name: /PayPal/ });
if (await paypalButton.isVisible()) {
await paypalButton.click();
}
}
async function stubPayPalSdk(page: import('@playwright/test').Page) {
await page.route('https://www.paypal.com/sdk/js**', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/javascript',
body: `
window.paypal = {
Buttons: function (config) {
window.__paypalButtonsConfig = config;
return {
render: function () {
// noop
},
};
},
};
`,
});
});
}
declare global {
interface Window {
__paypalButtonsConfig?: {
createOrder: () => Promise<string>;
onApprove: (data: { orderID: string }) => Promise<void>;
onError?: (error: unknown) => void;
};
}
}