Gate testing API for staging E2E
This commit is contained in:
@@ -13,7 +13,7 @@ class TestCheckoutController extends Controller
|
|||||||
{
|
{
|
||||||
public function latest(Request $request): JsonResponse
|
public function latest(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
abort_unless(app()->environment(['local', 'testing']), 404);
|
abort_unless(config('e2e.testing_enabled'), 404);
|
||||||
|
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'email' => ['nullable', 'string', 'email'],
|
'email' => ['nullable', 'string', 'email'],
|
||||||
@@ -66,7 +66,7 @@ class TestCheckoutController extends Controller
|
|||||||
CheckoutWebhookService $webhooks,
|
CheckoutWebhookService $webhooks,
|
||||||
CheckoutSession $session
|
CheckoutSession $session
|
||||||
): JsonResponse {
|
): JsonResponse {
|
||||||
abort_unless(app()->environment(['local', 'testing']), 404);
|
abort_unless(config('e2e.testing_enabled'), 404);
|
||||||
|
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'event_type' => ['nullable', 'string'],
|
'event_type' => ['nullable', 'string'],
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class TestCouponController extends Controller
|
|||||||
{
|
{
|
||||||
public function store(Request $request): JsonResponse
|
public function store(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
abort_unless(app()->environment(['local', 'testing']), 404);
|
abort_unless(config('e2e.testing_enabled'), 404);
|
||||||
|
|
||||||
$payload = $request->input('coupons');
|
$payload = $request->input('coupons');
|
||||||
$definitions = collect(is_array($payload) ? $payload : [])
|
$definitions = collect(is_array($payload) ? $payload : [])
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class TestEventController extends Controller
|
|||||||
{
|
{
|
||||||
public function joinToken(Request $request, EventJoinTokenService $tokens): JsonResponse
|
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([
|
$validated = $request->validate([
|
||||||
'event_id' => ['nullable', 'integer'],
|
'event_id' => ['nullable', 'integer'],
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class TestGuestEventController extends Controller
|
|||||||
{
|
{
|
||||||
public function store(Request $request, EventJoinTokenService $joinTokens): JsonResponse
|
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([
|
$validated = $request->validate([
|
||||||
'slug' => ['nullable', 'string', 'max:100'],
|
'slug' => ['nullable', 'string', 'max:100'],
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class TestMailboxController extends Controller
|
|||||||
{
|
{
|
||||||
public function index(): JsonResponse
|
public function index(): JsonResponse
|
||||||
{
|
{
|
||||||
abort_unless(app()->environment(['local', 'testing']), 404);
|
abort_unless(config('e2e.testing_enabled'), 404);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'data' => Mailbox::all(),
|
'data' => Mailbox::all(),
|
||||||
@@ -19,7 +19,7 @@ class TestMailboxController extends Controller
|
|||||||
|
|
||||||
public function destroy(): JsonResponse
|
public function destroy(): JsonResponse
|
||||||
{
|
{
|
||||||
abort_unless(app()->environment(['local', 'testing']), 404);
|
abort_unless(config('e2e.testing_enabled'), 404);
|
||||||
|
|
||||||
Mailbox::flush();
|
Mailbox::flush();
|
||||||
|
|
||||||
|
|||||||
24
app/Http/Middleware/EnsureE2ETestingAccess.php
Normal file
24
app/Http/Middleware/EnsureE2ETestingAccess.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class EnsureE2ETestingAccess
|
||||||
|
{
|
||||||
|
public function handle(Request $request, Closure $next): Response
|
||||||
|
{
|
||||||
|
if (! config('e2e.testing_enabled')) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = config('e2e.testing_token');
|
||||||
|
if ($token && $request->header('X-Testing-Token') !== $token) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -141,7 +141,7 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
[DispatchGuestNotificationPush::class, 'handle']
|
[DispatchGuestNotificationPush::class, 'handle']
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($this->app->environment(['local', 'testing'])) {
|
if (config('e2e.testing_enabled')) {
|
||||||
EventFacade::listen(
|
EventFacade::listen(
|
||||||
MessageSent::class,
|
MessageSent::class,
|
||||||
[Mailbox::class, 'record']
|
[Mailbox::class, 'record']
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class Mailbox
|
|||||||
|
|
||||||
public static function record(MessageSent $event): void
|
public static function record(MessageSent $event): void
|
||||||
{
|
{
|
||||||
if (! app()->environment(['local', 'testing'])) {
|
if (! config('e2e.testing_enabled')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
config/e2e.php
Normal file
6
config/e2e.php
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'testing_enabled' => env('E2E_TESTING_ENABLED', in_array(env('APP_ENV'), ['local', 'testing'], true)),
|
||||||
|
'testing_token' => env('E2E_TESTING_TOKEN'),
|
||||||
|
];
|
||||||
@@ -10,6 +10,9 @@ export default defineConfig({
|
|||||||
reporter: 'html',
|
reporter: 'html',
|
||||||
use: {
|
use: {
|
||||||
baseURL: process.env.E2E_BASE_URL ?? 'https://test-y0k0.fotospiel.app',
|
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',
|
trace: 'on-first-retry',
|
||||||
headless: true,
|
headless: true,
|
||||||
screenshot: 'only-on-failure',
|
screenshot: 'only-on-failure',
|
||||||
|
|||||||
@@ -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';
|
require __DIR__.'/testing.php';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
use App\Http\Controllers\Testing\TestCheckoutController;
|
use App\Http\Controllers\Testing\TestCheckoutController;
|
||||||
use App\Http\Controllers\Testing\TestCouponController;
|
use App\Http\Controllers\Testing\TestCouponController;
|
||||||
use App\Http\Controllers\Testing\TestGuestEventController;
|
|
||||||
use App\Http\Controllers\Testing\TestEventController;
|
use App\Http\Controllers\Testing\TestEventController;
|
||||||
|
use App\Http\Controllers\Testing\TestGuestEventController;
|
||||||
use App\Http\Controllers\Testing\TestMailboxController;
|
use App\Http\Controllers\Testing\TestMailboxController;
|
||||||
|
use App\Http\Middleware\EnsureE2ETestingAccess;
|
||||||
use Illuminate\Support\Facades\Route;
|
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::get('/mailbox', [TestMailboxController::class, 'index'])->name('testing.mailbox.index');
|
||||||
Route::delete('/mailbox', [TestMailboxController::class, 'destroy'])->name('testing.mailbox.destroy');
|
Route::delete('/mailbox', [TestMailboxController::class, 'destroy'])->name('testing.mailbox.destroy');
|
||||||
|
|
||||||
|
|||||||
36
tests/Feature/TestingApiAccessTest.php
Normal file
36
tests/Feature/TestingApiAccessTest.php
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class TestingApiAccessTest extends TestCase
|
||||||
|
{
|
||||||
|
public function test_testing_routes_are_blocked_when_disabled(): void
|
||||||
|
{
|
||||||
|
config([
|
||||||
|
'e2e.testing_enabled' => 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']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,8 +17,8 @@ test.describe('Marketing auth flows', () => {
|
|||||||
await page.getByLabel(/Telefon/i).fill('+49123456789');
|
await page.getByLabel(/Telefon/i).fill('+49123456789');
|
||||||
await page.getByLabel(/Adresse/i).fill('Teststr. 1, 12345 Berlin');
|
await page.getByLabel(/Adresse/i).fill('Teststr. 1, 12345 Berlin');
|
||||||
await page.getByLabel(/Username/i).fill(username);
|
await page.getByLabel(/Username/i).fill(username);
|
||||||
await page.getByLabel(/^Passwort$/i).fill(password);
|
await page.fill('input[name="password"]', password);
|
||||||
await page.getByLabel(/Passwort bestätigen/i).fill(password);
|
await page.fill('input[name="password_confirmation"]', password);
|
||||||
await page.locator('#privacy_consent').check();
|
await page.locator('#privacy_consent').check();
|
||||||
|
|
||||||
await page.getByRole('button', { name: /^Registrieren$/i }).click();
|
await page.getByRole('button', { name: /^Registrieren$/i }).click();
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ test.describe('Marketing hero CTA smoke', () => {
|
|||||||
|
|
||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
|
|
||||||
const cta = page.getByRole('link', {
|
const cta = page.locator('#hero').getByRole('link', {
|
||||||
name: /Pakete entdecken|Jetzt loslegen|Discover Packages|Get started now/i,
|
name: /Pakete ansehen|Pakete entdecken|Discover Packages/i,
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(cta).toBeVisible();
|
await expect(cta).toBeVisible();
|
||||||
@@ -22,12 +22,12 @@ test.describe('Marketing hero CTA smoke', () => {
|
|||||||
await page.goto('/packages');
|
await page.goto('/packages');
|
||||||
|
|
||||||
const cta = page.getByRole('link', {
|
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 expect(cta).toBeVisible();
|
||||||
await cta.click();
|
await cta.click();
|
||||||
|
|
||||||
await expect(page.locator('#endcustomer')).toBeVisible();
|
await expect(page.locator('#packages-showcase')).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ test.describe('Marketing frontend smoke', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await expect(page.getByRole('heading', { level: 1, name: /Dein Event|Fotospiel/i })).toBeVisible();
|
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 expect(heroCta).toBeVisible();
|
||||||
|
|
||||||
await heroCta.click();
|
await heroCta.click();
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ test.describe('Standard package checkout with Paddle completion', () => {
|
|||||||
await page.goto('/de/packages');
|
await page.goto('/de/packages');
|
||||||
|
|
||||||
const standardDetailsButton = page
|
const standardDetailsButton = page
|
||||||
.getByRole('heading', { name: /^Standard$/ })
|
.locator('[data-slot="card"]')
|
||||||
.locator('..')
|
.filter({ hasText: /Standard/i })
|
||||||
.getByRole('button', { name: /Details/i })
|
.getByRole('button', { name: /Details ansehen|Details anzeigen|View details/i })
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
await expect(standardDetailsButton).toBeVisible();
|
await expect(standardDetailsButton).toBeVisible();
|
||||||
|
|||||||
Reference in New Issue
Block a user