all())->not->toBeEmpty(); foreach (array_keys($previews->all()) as $key) { $html = $previews->make($key)?->render(); expect($html)->toBeString() ->and(strlen((string) $html))->toBeGreaterThan(500); } }); it('leaves nothing behind in the database', function () { // make(), never create(). A preview must not leave an invoice, an order or a // maintenance window lying about — least of all one carrying a number drawn // from a series. $before = [ 'invoices' => Invoice::query()->count(), 'orders' => Order::query()->count(), 'windows' => MaintenanceWindow::query()->count(), ]; foreach (array_keys(app(MailPreviews::class)->all()) as $key) { app(MailPreviews::class)->make($key)?->render(); } expect(Invoice::query()->count())->toBe($before['invoices']) ->and(Order::query()->count())->toBe($before['orders']) ->and(MaintenanceWindow::query()->count())->toBe($before['windows']); }); it('answers nothing for a key nobody knows, rather than throwing', function () { // Reached from a URL, and a URL is a string somebody can retype. expect(app(MailPreviews::class)->make('gibt-es-nicht'))->toBeNull(); }); it('sends a preview to the operator own address and to nobody else', function () { Mail::fake(); $operator = operator('Owner'); Livewire::actingAs($operator, 'operator') ->test(MailPreview::class) ->call('sendToMe', 'verify-email') ->assertDispatched('notify'); // SENT, not queued. Every mailable here implements ShouldQueue, so send() // would have deferred to the queue — putting a failure in a worker's log // while the button reported success to somebody who is waiting for the mail. Mail::assertSent(App\Mail\VerifyEmailMail::class, fn ($mail) => $mail->hasTo($operator->email)); Mail::assertNotQueued(App\Mail\VerifyEmailMail::class); }); it('has no recipient field, because that would be a form for mailing strangers', function () { $component = Illuminate\Support\Facades\File::get(app_path('Livewire/Admin/MailPreview.php')); // The address comes from the signed-in operator, never from the request. expect($component)->toContain("Auth::guard('operator')->user()") ->and($component)->not->toContain('public string $to'); }); it('serves the rendered mail as the document itself', function () { // Not escaped into a console page: a mail is a whole document with its own // background, and judging it inside a frame judges the frame. $this->actingAs(operator('Owner'), 'operator') ->get(route('admin.mail.preview.show', 'verify-email')) ->assertOk() ->assertSee('max-width:600px', false); }); it('keeps the preview to operators who may manage mail', function () { Livewire::actingAs(operator('Read-only'), 'operator') ->test(MailPreview::class) ->assertForbidden(); $this->actingAs(operator('Read-only'), 'operator') ->get(route('admin.mail.preview.show', 'verify-email')) ->assertForbidden(); }); it('answers 404 for a preview that does not exist', function () { $this->actingAs(operator('Owner'), 'operator') ->get(route('admin.mail.preview.show', 'gibt-es-nicht')) ->assertNotFound(); }); // ---- What made this necessary ---- it('builds the mail layout fluid, so a phone does not scroll sideways', function () { // width="600" plus width:600px forced every phone to scroll to read a // sentence. A mail cannot rely on a media query — Outlook renders with Word // and drops