seedLookupData(); $user = $this->user(['name' => 'Browser Attachment User']); $subject = 'Browser attachment ticket ' . uniqid('', true); $filePath = $this->makeBrowserUploadFile('browser-upload-proof.log', 'Browser upload proof file.'); $this->browse(function (Browser $browser) use ($user, $fixtures, $subject, $filePath): void { $browser->loginAs($user) ->visitRoute('helpdesk.tickets.create') ->waitFor('@helpdesk-ticket-create', 10) ->assertSee('Create ticket') ->type('@ticket-subject', $subject); $browser->attach('#attachments', $filePath); $this->waitForHelpdeskUploadToFinish($browser); $browser->type('@ticket-subject', $subject); $this->setHelpdeskSelect($browser, 'board_id', $fixtures['board']->id); $this->setHelpdeskSelect($browser, 'priority_id', $fixtures['high']->id); $this->setHelpdeskSelect($browser, 'category_id', $fixtures['category']->id); $this->setHelpdeskSelect($browser, 'team_id', $fixtures['team']->id); $this->setHelpdeskSelect($browser, 'project_id', $fixtures['project']->id); $this->setHelpdeskEditor( $browser, 'ticket-body-editor', 'This browser ticket proves attachments can be uploaded and saved.', ); $this->submitTicketCreateComponent($browser); $this->waitForTicketCreated($browser, $subject); $browser->visitRoute('helpdesk.tickets.index') ->waitForText($subject, 10) ->assertPathIs('/helpdesk/tickets') ->assertSee($subject); }); $ticket = Ticket::query() ->where('subject', $subject) ->firstOrFail(); $attachment = Attachment::query() ->where('ticket_id', $ticket->id) ->whereNull('comment_id') ->where('original_name', 'browser-upload-proof.log') ->firstOrFail(); self::assertSame($ticket->id, (int) $attachment->ticket_id); } public function test_user_can_create_ticket_with_multiple_attachments_in_browser(): void { $fixtures = $this->seedLookupData(); $user = $this->user(['name' => 'Browser Multi Attachment User']); $subject = 'Browser multiple attachment ticket ' . uniqid('', true); $firstFile = $this->makeBrowserUploadFile('browser-first-proof.txt', 'First browser upload proof file.'); $secondFile = $this->makeBrowserUploadFile('browser-second-proof.csv', 'name,value' . PHP_EOL . 'proof,1'); $this->browse(function (Browser $browser) use ($user, $fixtures, $subject, $firstFile, $secondFile): void { $browser->loginAs($user) ->visitRoute('helpdesk.tickets.create') ->waitFor('@helpdesk-ticket-create', 10) ->assertSee('Create ticket') ->type('@ticket-subject', $subject); $browser->attach('#attachments', $firstFile . "\n" . $secondFile); $this->waitForHelpdeskUploadToFinish($browser); $browser->type('@ticket-subject', $subject); $this->setHelpdeskSelect($browser, 'board_id', $fixtures['board']->id); $this->setHelpdeskSelect($browser, 'priority_id', $fixtures['normal']->id); $this->setHelpdeskSelect($browser, 'category_id', $fixtures['category']->id); $this->setHelpdeskSelect($browser, 'team_id', $fixtures['team']->id); $this->setHelpdeskSelect($browser, 'project_id', $fixtures['project']->id); $this->setHelpdeskEditor( $browser, 'ticket-body-editor', 'This browser ticket proves multiple attachments can be uploaded and saved.', ); $this->submitTicketCreateComponent($browser); $this->waitForTicketCreated($browser, $subject); $browser->visitRoute('helpdesk.tickets.index') ->waitForText($subject, 10) ->assertPathIs('/helpdesk/tickets') ->assertSee($subject); }); $ticket = Ticket::query() ->where('subject', $subject) ->firstOrFail(); self::assertSame( 2, Attachment::query()->where('ticket_id', $ticket->id)->whereNull('comment_id')->count(), ); foreach (['browser-first-proof.txt', 'browser-second-proof.csv'] as $name) { $this->assertDatabaseHas('helpdesk_attachments', [ 'ticket_id' => $ticket->id, 'comment_id' => null, 'original_name' => $name, ]); } } private function makeBrowserUploadFile(string $name, string $contents): string { $directory = storage_path('framework/testing/helpdesk-browser-uploads'); if (! is_dir($directory)) { mkdir($directory, 0777, true); } $path = $directory . DIRECTORY_SEPARATOR . $name; file_put_contents($path, $contents); return $path; } private function setHelpdeskEditor(Browser $browser, string $dusk, string $text): void { $encodedDusk = json_encode($dusk, JSON_THROW_ON_ERROR); $encodedText = json_encode($text, JSON_THROW_ON_ERROR); $browser->script(<<pause(500); } private function setHelpdeskSelect(Browser $browser, string $model, int|string|null $value): void { $encodedModel = json_encode($model, JSON_THROW_ON_ERROR); $encodedValue = json_encode($value === null ? '' : (string) $value, JSON_THROW_ON_ERROR); $browser->script(<<pause(250); } private function submitTicketCreateComponent(Browser $browser): void { $browser->waitUsing(10, 100, static function () use ($browser): bool { return (bool) $browser->script(<<<'JS' const button = document.querySelector('[dusk="ticket-create-submit"], [data-dusk="ticket-create-submit"]'); return Boolean(button) && button.disabled === false; JS)[0]; }, 'The helpdesk ticket create form was not ready to submit.'); $browser->script(<<<'JS' const root = document.querySelector('[dusk="helpdesk-ticket-create"], [data-dusk="helpdesk-ticket-create"]'); if (! root) { throw new Error('Helpdesk ticket create root was not found.'); } let componentRoot = root; while (componentRoot && ! componentRoot.hasAttribute('wire:id')) { componentRoot = componentRoot.parentElement; } if (! componentRoot || ! window.Livewire) { throw new Error('Helpdesk Livewire create component was not found.'); } const component = window.Livewire.find(componentRoot.getAttribute('wire:id')); if (! component) { throw new Error('Helpdesk Livewire create component instance was not found.'); } component.call('save'); JS); } private function waitForHelpdeskUploadToFinish(Browser $browser): void { $browser->waitUsing(30, 250, static function () use ($browser): bool { return (bool) $browser->script(<<<'JS' const input = document.querySelector('#attachments'); const button = document.querySelector('[dusk="ticket-create-submit"], [data-dusk="ticket-create-submit"]'); const selected = document.querySelector('[dusk="ticket-create-selected-attachments"], [data-dusk="ticket-create-selected-attachments"]'); return Boolean(input) && input.files.length > 0 && Boolean(selected) && Boolean(button) && button.disabled === false; JS)[0]; }, 'The Livewire temporary attachment upload did not finish before submit.'); } private function waitForTicketCreated(Browser $browser, string $subject): void { $browser->waitUsing(20, 250, static fn (): bool => Ticket::query() ->where('subject', $subject) ->exists(), 'The browser did not create the helpdesk ticket before the timeout.'); } }