create(['email' => 'gekuendigt@example.test', 'email_verified_at' => now()]); $customer = Customer::factory()->create([ 'email' => 'gekuendigt@example.test', 'user_id' => $user->id, 'status' => 'active', ]); $order = Order::factory()->create(['customer_id' => $customer->id, 'plan' => 'team']); $instance = Instance::factory()->create([ 'customer_id' => $customer->id, 'order_id' => $order->id, 'plan' => 'team', 'status' => $status, 'subdomain' => 'gekuendigt', 'cancel_requested_at' => now()->subDays(2), 'service_ends_at' => $endetIn, ]); return [$user, $instance->refresh()]; } /** * Ein Kunde mit einer laufenden Instanz, die niemand gekündigt hat — * dieselbe Form wie gekuendigteInstanz(), nur ohne Kündigung. * * @return array{0: User, 1: Instance} */ function laufendeInstanz(): array { $user = User::factory()->create(['email' => 'laeuft@example.test', 'email_verified_at' => now()]); $customer = Customer::factory()->create([ 'email' => 'laeuft@example.test', 'user_id' => $user->id, 'status' => 'active', ]); $order = Order::factory()->create(['customer_id' => $customer->id, 'plan' => 'team']); $instance = Instance::factory()->create([ 'customer_id' => $customer->id, 'order_id' => $order->id, 'plan' => 'team', 'status' => 'active', 'subdomain' => 'laeuft', ]); return [$user, $instance->refresh()]; } it('zeigt nach einer Kuendigung, wie lange der Zugang noch steht', function () { [$user, $instance] = gekuendigteInstanz(endetIn: now()->addDays(12)); Livewire::actingAs($user)->test(Dashboard::class) ->assertSee(__('dashboard.ending.title')) ->assertSee($instance->service_ends_at->local()->isoFormat('LL')); }); it('zeigt den Streifen nicht, solange nicht gekuendigt wurde', function () { [$user] = laufendeInstanz(); Livewire::actingAs($user)->test(Dashboard::class) ->assertDontSee(__('dashboard.ending.title')); }); it('zeigt ihn auch nach dem Laufzeitende nicht mehr', function () { // Danach ist der Zugang zu, und ein Streifen, der eine Restzeit nennt, // wäre schlicht falsch. Was dann gilt, sagt die Seite an anderer Stelle. [$user] = gekuendigteInstanz(endetIn: now()->subDay(), status: 'ended'); Livewire::actingAs($user)->test(Dashboard::class) ->assertDontSee(__('dashboard.ending.title')); }); it('nennt keine Restzeit, die schon abgelaufen ist', function () { // Der Fall zwischen den Stühlen: das Laufzeitende ist vorbei, aber der // stündliche Lauf hat die Instanz noch nicht auf `ended` gestellt. Eine // Zeile "noch -3 Stunden" ist die Sorte Fehler, die niemand meldet und // jeder sieht. // // Korrektur gegenüber dem Zettel: `not->toContain('-')` über das ganze // Dokument trägt nicht — ein Datum und eine CSS-Klasse enthalten ebenso // Bindestriche, und die Prüfung wäre aus dem falschen Grund rot oder // grün. Sie zielt stattdessen auf die Restzeit selbst: der Streifen darf // gar nicht erst erscheinen, wenn das Laufzeitende vorbei ist — unabhängig // davon, ob der Status das schon nachvollzogen hat. Genau das ist die // Prüfung, die eine Implementierung ertappt, die sich allein auf den // Status verlässt und das Datum ignoriert. [$user] = gekuendigteInstanz(endetIn: now()->subHours(3)); Livewire::actingAs($user)->test(Dashboard::class) ->assertDontSee(__('dashboard.ending.title')); }); it('nennt die Restzeit in ganzen Tagen, solange mehr als ein Tag bleibt', function () { // Nicht im Zettel geprüft, aber ausdrücklich gefordert: "noch 12 Tage" // liest sich, "noch 11 Tage 7 Stunden" nicht — abgerundet auf ganze Tage. [$user] = gekuendigteInstanz(endetIn: now()->addDays(3)->addHours(5)); Livewire::actingAs($user)->test(Dashboard::class) ->assertSee(trans_choice('dashboard.ending.remaining_days', 3)); }); it('zaehlt am letzten Tag in Stunden, nicht mehr in Tagen', function () { [$user] = gekuendigteInstanz(endetIn: now()->addHours(5)); Livewire::actingAs($user)->test(Dashboard::class) ->assertSee(trans_choice('dashboard.ending.remaining_hours', 5)); }); it('traegt die Antwort zum Export und laesst sie aendern', function () { [$user, $instance] = gekuendigteInstanz(endetIn: now()->addDays(12)); $instance->update(['export_wish' => false]); Livewire::actingAs($user)->test(Dashboard::class) ->assertSee(__('dashboard.ending.export_no')) ->call('setExportWish', true); expect($instance->fresh()->export_wish)->toBeTrue(); }); it('laesst die Antwort nach dem Laufzeitende nicht mehr aendern', function () { // Aufgabe 4: danach ist die Instanz `ended`, die Adresse eingezogen, und // ob ein Export vorbereitet wurde, ist entschieden. Ein Aufruf, der dort // noch etwas verspricht, wäre die nächste Attrappe — geprüft direkt gegen // die Methode, nicht nur gegen den (dann ohnehin nicht mehr sichtbaren) // Schalter im Streifen: setExportWish() ist über /livewire/update auch am // Formular vorbei erreichbar. [$user, $instance] = gekuendigteInstanz(endetIn: now()->subDay(), status: 'ended'); $instance->update(['export_wish' => false]); Livewire::actingAs($user)->test(Dashboard::class) ->call('setExportWish', true); expect($instance->fresh()->export_wish)->toBeFalse(); });