create(['name' => 'Dr. S. Berger']); $customer = Customer::factory()->create(['user_id' => $user->id, 'email' => $user->email]); return [$user, $customer]; } it('records a request and shows it back to the customer', function () { [$user, $customer] = supportCustomer(); Livewire::actingAs($user) ->test(NewSupportRequest::class) ->set('category', 'technical') ->set('subject', 'Kalendersynchronisation mit Outlook') ->set('body', 'Termine werden bei zwei Mitarbeitern nicht mehr übernommen.') ->call('save') ->assertHasNoErrors(); $request = SupportRequest::query()->where('customer_id', $customer->id)->sole(); expect($request->status)->toBe('open') // The person, not the account: on a shared login the account name says // nothing about who is actually asking. ->and($request->reported_by)->toBe('Dr. S. Berger'); Livewire::actingAs($user) ->test(Support::class) ->assertSee('Kalendersynchronisation mit Outlook') ->assertSee(__('support.status_open')); }); it('attaches the instance so nobody has to ask which server', function () { [$user, $customer] = supportCustomer(); $instance = Instance::factory()->create(['customer_id' => $customer->id, 'status' => 'active']); Livewire::actingAs($user) ->test(NewSupportRequest::class) ->set('subject', 'Ein Betreff') ->set('body', 'Eine Nachricht, die lang genug ist.') ->call('save'); expect(SupportRequest::query()->sole()->instance_id)->toBe($instance->id); }); it('still takes a request from somebody whose cloud is not built yet', function () { // A question can arrive before provisioning finishes, and that is exactly // when somebody is most likely to have one. [$user] = supportCustomer(); Livewire::actingAs($user) ->test(NewSupportRequest::class) ->set('subject', 'Wann geht es los?') ->set('body', 'Wir haben gestern bestellt und würden gerne planen.') ->call('save') ->assertHasNoErrors(); expect(SupportRequest::query()->sole()->instance_id)->toBeNull(); }); it('refuses an empty or barely-written request', function () { [$user] = supportCustomer(); Livewire::actingAs($user) ->test(NewSupportRequest::class) ->set('subject', '') ->set('body', 'kurz') ->call('save') ->assertHasErrors(['subject', 'body']); expect(SupportRequest::query()->count())->toBe(0); }); it('refuses a category that is not on the form', function () { [$user] = supportCustomer(); Livewire::actingAs($user) ->test(NewSupportRequest::class) ->set('category', 'urgent-vip') ->set('subject', 'Ein Betreff') ->set('body', 'Eine Nachricht, die lang genug ist.') ->call('save') ->assertHasErrors('category'); }); it('shows a customer only their own requests', function () { [$user] = supportCustomer(); [, $other] = supportCustomer(); SupportRequest::create([ 'customer_id' => $other->id, 'subject' => 'Fremde Anfrage', 'category' => 'billing', 'body' => 'Gehört jemand anderem.', 'status' => 'open', ]); Livewire::actingAs($user) ->test(Support::class) ->assertDontSee('Fremde Anfrage') ->assertSee(__('support.empty_title')); }); it('counts only what is still open', function () { [$user, $customer] = supportCustomer(); foreach (['open', 'in_progress', 'answered', 'closed'] as $status) { SupportRequest::create([ 'customer_id' => $customer->id, 'subject' => 'Anfrage '.$status, 'category' => 'other', 'body' => 'Eine Nachricht, die lang genug ist.', 'status' => $status, ]); } Livewire::actingAs($user) ->test(Support::class) // open + in_progress. Answered and closed are done with. ->assertViewHas('openCount', 2); }); it('says something useful when there is nothing to show', function () { // An empty state that names the next step, rather than "no data". [$user] = supportCustomer(); Livewire::actingAs($user) ->test(Support::class) ->assertSee(__('support.empty_title')) ->assertSee(__('support.empty_body')) ->assertSee(__('support.new')); }); it('carries no invented ticket titles', function () { // Three fictional tickets used to live in the translation file and render // as though they were the customer's own. foreach (['de', 'en'] as $locale) { $keys = array_keys(include base_path("lang/{$locale}/support.php")); expect($keys)->not->toContain('ticket_migration') ->and($keys)->not->toContain('ticket_training') ->and($keys)->not->toContain('ticket_smtp') ->and($keys)->not->toContain('new_toast'); } });