- Buchungs-Seite zeigt das echte 4-Tier-Raster aus plans (Monat/Jahr-
Toggle, Jahrespreis als "2 Monate gratis") mit Checkout-Buttons,
Einzel-PM als separaten No-Abo-Block und Enterprise-Hinweis;
Credit-Konzept-Mock entfernt (Credits folgen mit 9I bzw. Phase 2)
- Aktueller-Tarif-Panel real: Abo (Preis, Kontingent, Kündigungsstatus),
Bestandstarif (unbegrenzt, nächste MAN-Rechnung), offene Einzelkäufe;
Kontingent-Kachel zeigt "Unbegrenzt" bei Bestandsschutz
- "Abo verwalten" über das Stripe Billing Portal
(me.checkout.billing-portal; Zahlungsmethode, Rechnungen, Kündigung)
- Aktive Buchungen + Verlauf aus echten Daten (Abo, Legacy-Vereinbarung,
offene/eingelöste Einzelkäufe mit PM-Verknüpfung)
- Tests: BookingsPageTest (9 Tests), PanelConsolidationTest angepasst;
Suite 519 passed / 4 skipped
- Doku: PHASE-9-Plan 9F ✅, Billing-Doku (Routen, Stripe Tax aktiviert),
STATUS-ABGLEICH, Checkliste, PROGRESS
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
159 lines
6 KiB
PHP
159 lines
6 KiB
PHP
<?php
|
|
|
|
use App\Models\Plan;
|
|
use App\Models\User;
|
|
use Database\Seeders\RolesAndPermissionsSeeder;
|
|
use Tests\TestCase;
|
|
|
|
beforeEach(function (): void {
|
|
/** @var TestCase $this */
|
|
$this->seed(RolesAndPermissionsSeeder::class);
|
|
});
|
|
|
|
test('legacy customer dashboard URL redirects to me dashboard', function () {
|
|
/** @var TestCase $this */
|
|
$this->get('/customer')
|
|
->assertRedirect('/admin/me');
|
|
});
|
|
|
|
test('legacy customer press releases URL redirects to me press releases', function () {
|
|
/** @var TestCase $this */
|
|
$this->get('/customer/press-releases')
|
|
->assertRedirect('/admin/me/press-releases');
|
|
|
|
$this->get('/customer/press-releases/create')
|
|
->assertRedirect('/admin/me/press-releases/create');
|
|
|
|
$this->get('/customer/press-releases/123')
|
|
->assertRedirect('/admin/me/press-releases/123');
|
|
|
|
$this->get('/customer/press-releases/123/edit')
|
|
->assertRedirect('/admin/me/press-releases/123/edit');
|
|
});
|
|
|
|
test('legacy customer settings URLs redirect to me area', function () {
|
|
/** @var TestCase $this */
|
|
$this->get('/customer/profile')->assertRedirect('/admin/me/profile');
|
|
$this->get('/customer/security')->assertRedirect('/admin/me/security');
|
|
$this->get('/customer/tokens')->assertRedirect('/admin/me/tokens');
|
|
$this->get('/customer/invoices')->assertRedirect('/admin/me/invoices');
|
|
$this->get('/customer/buchungen-add-ons')->assertRedirect('/admin/me/buchungen-add-ons');
|
|
$this->get('/customer/pressemappen')->assertRedirect('/admin/me/firmen');
|
|
$this->get('/customer/pressemappen/123')->assertRedirect('/admin/me/firmen/123');
|
|
});
|
|
|
|
test('me dashboard requires authentication', function () {
|
|
/** @var TestCase $this */
|
|
$this->get(route('me.dashboard'))
|
|
->assertRedirect(route('login'));
|
|
});
|
|
|
|
test('customer can access me dashboard but not admin dashboard', function () {
|
|
/** @var TestCase $this */
|
|
$customer = User::factory()->create(['is_active' => true]);
|
|
$customer->assignRole('customer');
|
|
|
|
$this->actingAs($customer);
|
|
|
|
$this->get(route('me.dashboard'))->assertSuccessful();
|
|
$this->get(route('dashboard'))->assertForbidden();
|
|
});
|
|
|
|
test('customer bookings page shows the tariff grid and single pm block', function () {
|
|
// Seit Phase 9F zeigt die Seite das echte Tarif-Raster mit
|
|
// Stripe-Checkout statt des Credit-Konzept-Mocks (Credits → Phase 9I/2).
|
|
/** @var TestCase $this */
|
|
$customer = User::factory()->create(['is_active' => true]);
|
|
$customer->assignRole('customer');
|
|
|
|
Plan::factory()->create([
|
|
'name' => 'Starter',
|
|
'monthly_price_cents' => 2900,
|
|
'press_release_quota' => 3,
|
|
]);
|
|
|
|
$this->actingAs($customer)
|
|
->get(route('me.bookings.index'))
|
|
->assertSuccessful()
|
|
->assertSee('Den passenden Tarif wählen')
|
|
->assertSee('Starter')
|
|
->assertSee('29 €')
|
|
->assertSee('2 Monate gratis')
|
|
->assertSee('Einzel-Pressemitteilung — ohne Abo')
|
|
->assertSee('Noch keine aktiven Buchungen');
|
|
});
|
|
|
|
test('admin can access both panel dashboards', function () {
|
|
/** @var TestCase $this */
|
|
$admin = User::factory()->create(['is_active' => true]);
|
|
$admin->assignRole('admin');
|
|
|
|
$this->actingAs($admin);
|
|
|
|
$this->get(route('dashboard'))->assertSuccessful();
|
|
$this->get(route('me.dashboard'))->assertSuccessful();
|
|
});
|
|
|
|
test('editor can access both panel dashboards', function () {
|
|
/** @var TestCase $this */
|
|
$editor = User::factory()->create(['is_active' => true]);
|
|
$editor->assignRole('editor');
|
|
|
|
$this->actingAs($editor);
|
|
|
|
$this->get(route('dashboard'))->assertSuccessful();
|
|
$this->get(route('me.dashboard'))->assertSuccessful();
|
|
});
|
|
|
|
test('inactive user cannot access panel', function () {
|
|
/** @var TestCase $this */
|
|
$user = User::factory()->create(['is_active' => false]);
|
|
$user->assignRole('customer');
|
|
|
|
$this->actingAs($user);
|
|
|
|
$this->get(route('me.dashboard'))->assertForbidden();
|
|
});
|
|
|
|
test('sidebar shows customer area for admins', function () {
|
|
/** @var TestCase $this */
|
|
$admin = User::factory()->create(['is_active' => true]);
|
|
$admin->assignRole('admin');
|
|
|
|
$this->actingAs($admin)
|
|
->get(route('dashboard'))
|
|
->assertSee('Mein Bereich')
|
|
->assertSee('Administration');
|
|
});
|
|
|
|
test('sidebar hides admin area for customers', function () {
|
|
/** @var TestCase $this */
|
|
$customer = User::factory()->create(['is_active' => true]);
|
|
$customer->assignRole('customer');
|
|
|
|
$this->actingAs($customer)
|
|
->get(route('me.dashboard'))
|
|
->assertSee('Mein Bereich')
|
|
->assertSee('Buchungen & Add-ons', false)
|
|
->assertSee('Finanzen')
|
|
->assertSee('Rechnungen')
|
|
->assertSee('Konto')
|
|
->assertSee('API & Integrationen', false)
|
|
->assertDontSee('Administration')
|
|
->assertDontSee('Footer-Codes');
|
|
});
|
|
|
|
test('all me routes resolve to expected admin paths', function () {
|
|
expect(route('me.dashboard', absolute: false))->toBe('/admin/me');
|
|
expect(route('me.press-releases.index', absolute: false))->toBe('/admin/me/press-releases');
|
|
expect(route('me.press-releases.create', absolute: false))->toBe('/admin/me/press-releases/create');
|
|
expect(route('me.press-releases.show', ['id' => 5], absolute: false))->toBe('/admin/me/press-releases/5');
|
|
expect(route('me.press-releases.edit', ['id' => 5], absolute: false))->toBe('/admin/me/press-releases/5/edit');
|
|
expect(route('me.press-kits.index', absolute: false))->toBe('/admin/me/firmen');
|
|
expect(route('me.press-kits.show', ['id' => 5], absolute: false))->toBe('/admin/me/firmen/5');
|
|
expect(route('me.bookings.index', absolute: false))->toBe('/admin/me/buchungen-add-ons');
|
|
expect(route('me.invoices.index', absolute: false))->toBe('/admin/me/invoices');
|
|
expect(route('me.tokens.index', absolute: false))->toBe('/admin/me/tokens');
|
|
expect(route('me.profile', absolute: false))->toBe('/admin/me/profile');
|
|
expect(route('me.security', absolute: false))->toBe('/admin/me/security');
|
|
});
|