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('10 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 page shows the empty state', function () { /** @var TestCase $this */ $this->actingAs(bookingsTestCustomer()); LivewireVolt::test('customer.bookings') ->assertSee('Noch kein aktiver Tarif') ->assertSee('Noch keine aktiven Buchungen'); }); 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('Abo: Pro') ->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).') ->assertSee('Unbegrenzt'); }); test('open and consumed single purchases appear in bookings 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('einlösbar') ->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!'); });