From 7ea34b3b20b6a2c6e13f86821134b3b3b3af3adc Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Sat, 3 Jan 2026 15:00:33 +0100 Subject: [PATCH] Gate testing API for staging E2E --- .../Testing/TestCheckoutController.php | 4 +-- .../Testing/TestCouponController.php | 2 +- .../Testing/TestEventController.php | 2 +- .../Testing/TestGuestEventController.php | 2 +- .../Testing/TestMailboxController.php | 4 +-- .../Middleware/EnsureE2ETestingAccess.php | 24 +++++++++++++ app/Providers/AppServiceProvider.php | 2 +- app/Testing/Mailbox.php | 2 +- config/e2e.php | 6 ++++ playwright.config.ts | 3 ++ routes/api.php | 2 +- routes/testing.php | 5 +-- tests/Feature/TestingApiAccessTest.php | 36 +++++++++++++++++++ tests/ui/auth/auth-flows.test.ts | 4 +-- tests/ui/purchase/checkout-hero-cta.test.ts | 8 ++--- tests/ui/purchase/marketing-smoke.test.ts | 4 ++- .../standard-package-checkout.test.ts | 6 ++-- 17 files changed, 94 insertions(+), 22 deletions(-) create mode 100644 app/Http/Middleware/EnsureE2ETestingAccess.php create mode 100644 config/e2e.php create mode 100644 tests/Feature/TestingApiAccessTest.php diff --git a/app/Http/Controllers/Testing/TestCheckoutController.php b/app/Http/Controllers/Testing/TestCheckoutController.php index c0893b4..114e762 100644 --- a/app/Http/Controllers/Testing/TestCheckoutController.php +++ b/app/Http/Controllers/Testing/TestCheckoutController.php @@ -13,7 +13,7 @@ class TestCheckoutController extends Controller { public function latest(Request $request): JsonResponse { - abort_unless(app()->environment(['local', 'testing']), 404); + abort_unless(config('e2e.testing_enabled'), 404); $validated = $request->validate([ 'email' => ['nullable', 'string', 'email'], @@ -66,7 +66,7 @@ class TestCheckoutController extends Controller CheckoutWebhookService $webhooks, CheckoutSession $session ): JsonResponse { - abort_unless(app()->environment(['local', 'testing']), 404); + abort_unless(config('e2e.testing_enabled'), 404); $validated = $request->validate([ 'event_type' => ['nullable', 'string'], diff --git a/app/Http/Controllers/Testing/TestCouponController.php b/app/Http/Controllers/Testing/TestCouponController.php index 50fef79..8a88e2f 100644 --- a/app/Http/Controllers/Testing/TestCouponController.php +++ b/app/Http/Controllers/Testing/TestCouponController.php @@ -16,7 +16,7 @@ class TestCouponController extends Controller { public function store(Request $request): JsonResponse { - abort_unless(app()->environment(['local', 'testing']), 404); + abort_unless(config('e2e.testing_enabled'), 404); $payload = $request->input('coupons'); $definitions = collect(is_array($payload) ? $payload : []) diff --git a/app/Http/Controllers/Testing/TestEventController.php b/app/Http/Controllers/Testing/TestEventController.php index 06a1931..2f330be 100644 --- a/app/Http/Controllers/Testing/TestEventController.php +++ b/app/Http/Controllers/Testing/TestEventController.php @@ -15,7 +15,7 @@ class TestEventController extends Controller { public function joinToken(Request $request, EventJoinTokenService $tokens): JsonResponse { - abort_unless(app()->environment(['local', 'testing']), 404); + abort_unless(config('e2e.testing_enabled'), 404); $validated = $request->validate([ 'event_id' => ['nullable', 'integer'], diff --git a/app/Http/Controllers/Testing/TestGuestEventController.php b/app/Http/Controllers/Testing/TestGuestEventController.php index 2cb2dad..9840059 100644 --- a/app/Http/Controllers/Testing/TestGuestEventController.php +++ b/app/Http/Controllers/Testing/TestGuestEventController.php @@ -20,7 +20,7 @@ class TestGuestEventController extends Controller { public function store(Request $request, EventJoinTokenService $joinTokens): JsonResponse { - abort_unless(app()->environment(['local', 'testing']), 404); + abort_unless(config('e2e.testing_enabled'), 404); $validated = $request->validate([ 'slug' => ['nullable', 'string', 'max:100'], diff --git a/app/Http/Controllers/Testing/TestMailboxController.php b/app/Http/Controllers/Testing/TestMailboxController.php index 9398dca..824c6e2 100644 --- a/app/Http/Controllers/Testing/TestMailboxController.php +++ b/app/Http/Controllers/Testing/TestMailboxController.php @@ -10,7 +10,7 @@ class TestMailboxController extends Controller { public function index(): JsonResponse { - abort_unless(app()->environment(['local', 'testing']), 404); + abort_unless(config('e2e.testing_enabled'), 404); return response()->json([ 'data' => Mailbox::all(), @@ -19,7 +19,7 @@ class TestMailboxController extends Controller public function destroy(): JsonResponse { - abort_unless(app()->environment(['local', 'testing']), 404); + abort_unless(config('e2e.testing_enabled'), 404); Mailbox::flush(); diff --git a/app/Http/Middleware/EnsureE2ETestingAccess.php b/app/Http/Middleware/EnsureE2ETestingAccess.php new file mode 100644 index 0000000..b15569b --- /dev/null +++ b/app/Http/Middleware/EnsureE2ETestingAccess.php @@ -0,0 +1,24 @@ +header('X-Testing-Token') !== $token) { + abort(404); + } + + return $next($request); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 473845d..f93b715 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -141,7 +141,7 @@ class AppServiceProvider extends ServiceProvider [DispatchGuestNotificationPush::class, 'handle'] ); - if ($this->app->environment(['local', 'testing'])) { + if (config('e2e.testing_enabled')) { EventFacade::listen( MessageSent::class, [Mailbox::class, 'record'] diff --git a/app/Testing/Mailbox.php b/app/Testing/Mailbox.php index aa277f7..06aab29 100644 --- a/app/Testing/Mailbox.php +++ b/app/Testing/Mailbox.php @@ -14,7 +14,7 @@ class Mailbox public static function record(MessageSent $event): void { - if (! app()->environment(['local', 'testing'])) { + if (! config('e2e.testing_enabled')) { return; } diff --git a/config/e2e.php b/config/e2e.php new file mode 100644 index 0000000..84489ef --- /dev/null +++ b/config/e2e.php @@ -0,0 +1,6 @@ + env('E2E_TESTING_ENABLED', in_array(env('APP_ENV'), ['local', 'testing'], true)), + 'testing_token' => env('E2E_TESTING_TOKEN'), +]; diff --git a/playwright.config.ts b/playwright.config.ts index e98ed5b..a42ade3 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -10,6 +10,9 @@ export default defineConfig({ reporter: 'html', use: { baseURL: process.env.E2E_BASE_URL ?? 'https://test-y0k0.fotospiel.app', + extraHTTPHeaders: process.env.E2E_TESTING_TOKEN + ? { 'X-Testing-Token': process.env.E2E_TESTING_TOKEN } + : undefined, trace: 'on-first-retry', headless: true, screenshot: 'only-on-failure', diff --git a/routes/api.php b/routes/api.php index 8612574..53a1d1b 100644 --- a/routes/api.php +++ b/routes/api.php @@ -353,6 +353,6 @@ Route::prefix('v1')->name('api.v1.')->group(function () { }); -if (app()->environment(['local', 'testing'])) { +if (config('e2e.testing_enabled')) { require __DIR__.'/testing.php'; } diff --git a/routes/testing.php b/routes/testing.php index a2c3e4b..16bf72a 100644 --- a/routes/testing.php +++ b/routes/testing.php @@ -2,12 +2,13 @@ use App\Http\Controllers\Testing\TestCheckoutController; use App\Http\Controllers\Testing\TestCouponController; -use App\Http\Controllers\Testing\TestGuestEventController; use App\Http\Controllers\Testing\TestEventController; +use App\Http\Controllers\Testing\TestGuestEventController; use App\Http\Controllers\Testing\TestMailboxController; +use App\Http\Middleware\EnsureE2ETestingAccess; use Illuminate\Support\Facades\Route; -Route::prefix('_testing')->group(function () { +Route::prefix('_testing')->middleware(EnsureE2ETestingAccess::class)->group(function () { Route::get('/mailbox', [TestMailboxController::class, 'index'])->name('testing.mailbox.index'); Route::delete('/mailbox', [TestMailboxController::class, 'destroy'])->name('testing.mailbox.destroy'); diff --git a/tests/Feature/TestingApiAccessTest.php b/tests/Feature/TestingApiAccessTest.php new file mode 100644 index 0000000..78ac37a --- /dev/null +++ b/tests/Feature/TestingApiAccessTest.php @@ -0,0 +1,36 @@ + false, + 'e2e.testing_token' => 'secret-token', + ]); + + $this->withHeader('X-Testing-Token', 'secret-token') + ->getJson('/api/_testing/mailbox') + ->assertNotFound(); + } + + public function test_testing_routes_require_token_when_enabled(): void + { + config([ + 'e2e.testing_enabled' => true, + 'e2e.testing_token' => 'secret-token', + ]); + + $this->getJson('/api/_testing/mailbox') + ->assertNotFound(); + + $this->withHeader('X-Testing-Token', 'secret-token') + ->getJson('/api/_testing/mailbox') + ->assertOk() + ->assertJsonStructure(['data']); + } +} diff --git a/tests/ui/auth/auth-flows.test.ts b/tests/ui/auth/auth-flows.test.ts index ba18c47..b7167af 100644 --- a/tests/ui/auth/auth-flows.test.ts +++ b/tests/ui/auth/auth-flows.test.ts @@ -17,8 +17,8 @@ test.describe('Marketing auth flows', () => { await page.getByLabel(/Telefon/i).fill('+49123456789'); await page.getByLabel(/Adresse/i).fill('Teststr. 1, 12345 Berlin'); await page.getByLabel(/Username/i).fill(username); - await page.getByLabel(/^Passwort$/i).fill(password); - await page.getByLabel(/Passwort bestätigen/i).fill(password); + await page.fill('input[name="password"]', password); + await page.fill('input[name="password_confirmation"]', password); await page.locator('#privacy_consent').check(); await page.getByRole('button', { name: /^Registrieren$/i }).click(); diff --git a/tests/ui/purchase/checkout-hero-cta.test.ts b/tests/ui/purchase/checkout-hero-cta.test.ts index 8aa54c5..25b772c 100644 --- a/tests/ui/purchase/checkout-hero-cta.test.ts +++ b/tests/ui/purchase/checkout-hero-cta.test.ts @@ -6,8 +6,8 @@ test.describe('Marketing hero CTA smoke', () => { await page.goto('/'); - const cta = page.getByRole('link', { - name: /Pakete entdecken|Jetzt loslegen|Discover Packages|Get started now/i, + const cta = page.locator('#hero').getByRole('link', { + name: /Pakete ansehen|Pakete entdecken|Discover Packages/i, }); await expect(cta).toBeVisible(); @@ -22,12 +22,12 @@ test.describe('Marketing hero CTA smoke', () => { await page.goto('/packages'); const cta = page.getByRole('link', { - name: /Pakete entdecken|Lieblingspaket sichern|Discover Packages|Explore top packages/i, + name: /Pakete entdecken|Discover Packages/i, }); await expect(cta).toBeVisible(); await cta.click(); - await expect(page.locator('#endcustomer')).toBeVisible(); + await expect(page.locator('#packages-showcase')).toBeVisible(); }); }); diff --git a/tests/ui/purchase/marketing-smoke.test.ts b/tests/ui/purchase/marketing-smoke.test.ts index 5b1a135..db302a6 100644 --- a/tests/ui/purchase/marketing-smoke.test.ts +++ b/tests/ui/purchase/marketing-smoke.test.ts @@ -18,7 +18,9 @@ test.describe('Marketing frontend smoke', () => { } await expect(page.getByRole('heading', { level: 1, name: /Dein Event|Fotospiel/i })).toBeVisible(); - const heroCta = page.getByRole('link', { name: /paket|packages|starten|ausprobieren/i }).first(); + const heroCta = page.locator('#hero').getByRole('link', { + name: /Pakete ansehen|Pakete entdecken|Discover Packages/i, + }); await expect(heroCta).toBeVisible(); await heroCta.click(); diff --git a/tests/ui/purchase/standard-package-checkout.test.ts b/tests/ui/purchase/standard-package-checkout.test.ts index 3f10ab7..43e2843 100644 --- a/tests/ui/purchase/standard-package-checkout.test.ts +++ b/tests/ui/purchase/standard-package-checkout.test.ts @@ -66,9 +66,9 @@ test.describe('Standard package checkout with Paddle completion', () => { await page.goto('/de/packages'); const standardDetailsButton = page - .getByRole('heading', { name: /^Standard$/ }) - .locator('..') - .getByRole('button', { name: /Details/i }) + .locator('[data-slot="card"]') + .filter({ hasText: /Standard/i }) + .getByRole('button', { name: /Details ansehen|Details anzeigen|View details/i }) .first(); await expect(standardDetailsButton).toBeVisible();