Files
WMS/Modules/Helpdesk/Tests/Browser/HelpdeskCreateTicketAttachmentBrowserTest.php
T

304 lines
11 KiB
PHP

<?php
declare(strict_types=1);
namespace Modules\Helpdesk\Tests\Browser;
use Laravel\Dusk\Browser;
use Modules\Helpdesk\Models\Attachment;
use Modules\Helpdesk\Models\Ticket;
use Modules\Helpdesk\Tests\Concerns\BuildsHelpdeskFixtures;
use Tests\DuskTestCase;
final class HelpdeskCreateTicketAttachmentBrowserTest extends DuskTestCase
{
use BuildsHelpdeskFixtures;
public function test_user_can_create_ticket_with_attachment_in_browser(): void
{
$fixtures = $this->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(<<<JS
const key = {$encodedDusk};
const text = {$encodedText};
const root = document.querySelector('[dusk="' + key + '"], [data-dusk="' + key + '"]');
if (! root) {
throw new Error('Helpdesk editor ' + key + ' was not found.');
}
const editor = root.matches('textarea, input, [contenteditable="true"]')
? root
: root.querySelector('textarea, input, [contenteditable="true"]');
if (! editor) {
throw new Error('Helpdesk editor content area ' + key + ' was not found.');
}
editor.focus();
if (editor.matches('textarea, input')) {
editor.value = text;
} else {
editor.textContent = text;
}
editor.dispatchEvent(new InputEvent('input', {
bubbles: true,
composed: true,
inputType: 'insertText',
data: text,
}));
editor.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
editor.dispatchEvent(new Event('blur', { bubbles: true, composed: true }));
const model = root.getAttribute('wire:model')
|| root.getAttribute('wire:model.live')
|| root.getAttribute('wire:model.defer')
|| root.getAttribute('data-wire-model')
|| editor.getAttribute('data-wire-model')
|| editor.getAttribute('data-helpdesk-editor-model')
|| editor.getAttribute('data-helpdesk-sync-target');
let componentRoot = root;
while (componentRoot && ! componentRoot.hasAttribute('wire:id')) {
componentRoot = componentRoot.parentElement;
}
if (model && componentRoot && window.Livewire) {
const component = window.Livewire.find(componentRoot.getAttribute('wire:id'));
if (component) {
component.set(model, text);
}
}
JS);
$browser->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(<<<JS
const model = {$encodedModel};
const value = {$encodedValue};
const select = document.querySelector('[data-wire-model="' + model + '"]');
if (! select) {
throw new Error('Helpdesk select ' + model + ' was not found.');
}
select.value = value;
select.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
if (window.jQuery) {
window.jQuery(select).val(value).trigger('change.select2').trigger('change.helpdesk-select2').trigger('change');
}
let componentRoot = select;
while (componentRoot && ! componentRoot.hasAttribute('wire:id')) {
componentRoot = componentRoot.parentElement;
}
if (componentRoot && window.Livewire) {
const component = window.Livewire.find(componentRoot.getAttribute('wire:id'));
if (component) {
component.set(model, value === '' ? null : value);
}
}
JS);
$browser->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.');
}
}