presseportale/tests/Feature/Billing/BookingsPageTest.php
Kevin Adametz 6a82e2a2a8 Buchungs-Seite: Feinschliff nach Review
- Aktueller-Tarif-Card erscheint erst mit vorhandener Buchung; die
  Kontingent-Kachel zeigt nur noch echte Zahlen (kein irreführendes
  "Unbegrenzt" vor dem Launch-Schalter)
- Tarif-Cards plakativer: Icon je Tarif, größerer Preis, Trennlinie vor
  den Leistungen, mehr Abstand zum größeren Buchen-Button
- "Prüfung und Veröffentlichung inklusive" statt "KI-Prüfung"
- "Aktive Buchungen"-Panel entfernt (redundant zum Tarif-Panel);
  Verlauf als eigene, durch Trennlinie abgegrenzte Sektion
- Tests angepasst; Suite 519 passed / 4 skipped

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 13:30:06 +00:00

156 lines
5.3 KiB
PHP

<?php
use App\Enums\UserPaymentOptionStatus;
use App\Models\Plan;
use App\Models\SinglePurchase;
use App\Models\User;
use App\Models\UserPaymentOption;
use App\Services\Billing\StripeCheckoutService;
use Database\Seeders\RolesAndPermissionsSeeder;
use Livewire\Volt\Volt as LivewireVolt;
use Tests\TestCase;
beforeEach(function (): void {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
});
function bookingsTestCustomer(): User
{
$user = User::factory()->create();
$user->assignRole('customer');
return $user;
}
test('the bookings page renders the active plans with checkout links', function () {
/** @var TestCase $this */
Plan::factory()->create([
'name' => 'Business',
'slug' => 'business',
'monthly_price_cents' => 4900,
'yearly_price_cents' => 49000,
'press_release_quota' => 10,
'daily_limit' => 2,
]);
Plan::factory()->inactive()->create(['name' => 'Versteckt']);
$this->actingAs(bookingsTestCustomer());
LivewireVolt::test('customer.bookings')
->assertSee('Business')
->assertSee('49 €')
->assertSee('490 €')
->assertSee('Pressemitteilungen pro Monat')
->assertSee('max. 2 Veröffentlichungen pro Tag')
->assertSee('2 Monate gratis')
->assertSee(route('me.checkout.subscription', ['planSlug' => 'business', 'interval' => 'monthly']), false)
->assertSee(route('me.checkout.subscription', ['planSlug' => 'business', 'interval' => 'yearly']), false)
->assertDontSee('Versteckt');
});
test('the single pm block links to its checkout', function () {
/** @var TestCase $this */
config()->set('billing.single_pm_stripe_price_id', 'price_test_single_pm');
$this->actingAs(bookingsTestCustomer());
LivewireVolt::test('customer.bookings')
->assertSee('Einzel-Pressemitteilung — ohne Abo')
->assertSee('19 €')
->assertSee(route('me.checkout.single-pm'), false);
});
test('without any booking the current tariff card is hidden', function () {
/** @var TestCase $this */
$this->actingAs(bookingsTestCustomer());
LivewireVolt::test('customer.bookings')
->assertDontSee('Aktueller Tarif')
->assertSee('Den passenden Tarif wählen')
->assertSee('Noch kein Buchungsverlauf');
});
test('a subscriber sees the current plan and the manage button', function () {
/** @var TestCase $this */
$user = bookingsTestCustomer();
$plan = Plan::factory()->create([
'name' => 'Pro',
'press_release_quota' => 25,
'stripe_price_id_monthly' => 'price_test_m_pro',
]);
subscribeUserToPlan($user, $plan);
$this->actingAs($user);
LivewireVolt::test('customer.bookings')
->assertSee('Ihr aktueller Tarif')
->assertSee('Abo verwalten')
->assertSee('Pressemitteilungen pro Monat')
->assertSee(route('me.checkout.billing-portal'), false);
});
test('a grandfathered legacy user sees the bestandstarif with unlimited quota', function () {
/** @var TestCase $this */
$user = bookingsTestCustomer();
UserPaymentOption::factory()->create([
'user_id' => $user->id,
'status' => UserPaymentOptionStatus::Grandfathered->value,
'current_period_end' => now()->addMonths(3),
'legacy_conditions' => ['name' => 'Presseverteiler Premium'],
]);
$this->actingAs($user);
LivewireVolt::test('customer.bookings')
->assertSee('Bestandstarif')
->assertSee('Presseverteiler Premium')
->assertSee('Unbegrenzte Pressemitteilungen (Bestandsschutz).');
});
test('open and consumed single purchases appear in the card and history', function () {
/** @var TestCase $this */
$user = bookingsTestCustomer();
SinglePurchase::factory()->paid()->create(['user_id' => $user->id]);
SinglePurchase::factory()->consumed()->create(['user_id' => $user->id]);
$this->actingAs($user);
LivewireVolt::test('customer.bookings')
->assertSee('Einzel-Pressemitteilung verfügbar')
->assertSee('eingelöst am');
});
test('the billing portal redirects without an active subscription', function () {
/** @var TestCase $this */
$this->actingAs(bookingsTestCustomer())
->get(route('me.checkout.billing-portal'))
->assertRedirect(route('me.bookings.index'))
->assertSessionHas('checkout-notice');
});
test('the billing portal forwards a subscriber to stripe', function () {
/** @var TestCase $this */
$user = bookingsTestCustomer();
$user->forceFill(['stripe_id' => 'cus_test_portal'])->save();
$plan = Plan::factory()->create(['stripe_price_id_monthly' => 'price_test_m_portal']);
subscribeUserToPlan($user, $plan);
$this->mock(StripeCheckoutService::class, function ($mock) {
$mock->shouldReceive('billingPortalUrl')
->once()
->andReturn('https://billing.stripe.com/p/session/test');
});
$this->actingAs($user)
->get(route('me.checkout.billing-portal'))
->assertRedirect('https://billing.stripe.com/p/session/test');
});
test('the checkout success banner is shown after returning from stripe', function () {
/** @var TestCase $this */
$this->actingAs(bookingsTestCustomer())
->get(route('me.bookings.index', ['checkout' => 'erfolg']))
->assertOk()
->assertSee('Vielen Dank für Ihre Buchung!');
});