actingAs(admin(), 'operator') ->get(route('admin.open-work')) ->assertOk() ->assertSee(__('open_work.title')); $html = $this->actingAs(admin(), 'operator')->get(route('admin.open-work'))->getContent(); foreach (OpenWork::all() as $item) { expect($html)->toContain(e($item['title'])); } }); it('nennt zu jedem Punkt wo, was fehlt und was passiert, wenn es so bleibt', function () { // Ohne den dritten Satz ist so eine Liste ein Wunschzettel: er entscheidet // die Reihenfolge, nicht das Gefühl. foreach (OpenWork::all() as $item) { foreach (['title', 'state', 'where', 'missing', 'risk'] as $field) { expect($item[$field] ?? '')->not->toBe('', "„{$item['title']}\" hat kein Feld {$field}."); } // Ein Zustand ohne Übersetzung wäre ein Kasten mit „open_work.state.x". expect(__('open_work.state.'.$item['state']))->not->toBe('open_work.state.'.$item['state']); } }); it('führt die zwei bezahlten Module, die nichts tun, als solche', function () { // Der Anlass für die ganze Seite. Verschwinden sie hier stillschweigend, // ist die Liste wertlos — sie kosten Geld, solange sie offen sind. $sold = collect(OpenWork::all())->where('state', OpenWork::SOLD)->pluck('title')->implode(' '); expect($sold)->toContain('Sicherungsort') ->and($sold)->toContain('Office'); }); it('steht jedem offen, der die Konsole öffnen darf', function () { // Kein eigenes Recht: wer hier arbeitet, soll wissen, worauf er sich // verlassen kann und worauf nicht. $entry = collect(Navigation::console()) ->flatMap(fn (array $g) => $g['items']) ->firstWhere(0, 'admin.open-work'); expect($entry)->not->toBeNull() ->and($entry[3])->toBeNull(); }); it('stellt das nach vorne, was gerade Schaden anrichtet', function () { // Nicht in der Reihenfolge, in der es geschrieben wurde — sondern danach, // was ein Punkt HEUTE anrichtet. „Verkauft, nicht geliefert" kostet in // diesem Moment Geld; „beschlossen" tut gar nichts, bis jemand anfängt. expect(array_keys(OpenWork::grouped())) ->toBe(array_values(array_filter( OpenWork::ORDER, fn (string $s) => (OpenWork::counts()[$s] ?? 0) > 0, ))); // Und der Sammelsatz oben nennt den dringendsten Zustand, der wirklich // vorkommt — nicht einen festen. expect(OpenWork::worst())->toBe(array_key_first(OpenWork::grouped())); }); it('lässt keine leere Gruppe stehen', function () { // Eine Überschrift ohne Inhalt liest sich wie ein Ladefehler. Derselbe // Fehler, der in der Seitenleiste schon einmal zuschlug. foreach (OpenWork::grouped() as $state => $items) { expect($items)->not->toBeEmpty("Gruppe {$state} ist leer."); } }); it('folgt der Formensprache der Bereitschaftsseite, nicht der eines Formulars', function () { // Die erste Fassung nahm das Muster der Anlegen-Seite: `lbl`-Etikett, // große Kopfzeile, pro Punkt eine eigene Karte. Für eine Liste mit // Zuständen gibt es in dieser Konsole längst ein Muster, und zwei Muster // für ein Problem sind zwei Sprachen. $page = File::get(resource_path('views/livewire/admin/open-work.blade.php')); expect($page)->toContain('divide-y divide-line') // Und keine Zustands-Plakette je Zeile: die Karte darüber sagt den // Zustand, alle Zeilen darin teilen ihn. Sie wiederholte dasselbe Wort // dreimal pro Bildschirm. ->and($page)->not->toContain('and($page)->not->toContain('lbl">') ->and($page)->not->toContain('min-[901px]:text-[30px]'); }); it('nutzt die Breite, die das Layout hergibt, statt in der Mitte aufzuhören', function () { // Gegen das AUSGELIEFERTE HTML geprüft, nicht gegen die Blade-Quelle: dort // steht der Klassenname auch im Kommentar, der erklärt, warum er weg ist — // und ein Test, der einen Kommentar liest, prüft nicht, was ankommt. $html = $this->actingAs(admin(), 'operator')->get(route('admin.open-work'))->getContent(); // Das Layout erlaubt 1320 px. Eine feste Zeichenbreite ließ rechts die // halbe Karte leer; die volle Breite ohne Grenze ergäbe rund 165 Zeichen // je Zeile. Zwei Spalten sind die Antwort auf beides. expect($html)->not->toContain('max-w-[76ch]') ->and($html)->toContain('lg:grid-cols-2'); });