testing Laravel Livewire components for the most part is a case of setting a property or method and testing the property has been set or can be seen. Testing a download is a little tricker to do.
I've seen some posts advising to check the headers and compare the content-disposition against an expected filename.
self::assertTrue($response->headers->get('content-disposition') == "attachment; filename='$expected_pdf_name'");
The problem in Laravel Livewire you don't have access to a $response object unless you assign the component but even that you don't have access to the headers array.
The Solution
There is now a dedicated assertFileDownloaded helper, I recommend using this instead available from V2.4.4 https://github.com/livewire/livewire/releases/tag/v2.4.4
Usage:
$timestamp = now();
$expected = "customers-$timestamp.xlsx";
Livewire::test(Customers::class)
->call('export', 'xlsx', $timestamp)
->assertFileDownloaded($expected);
Older version:
Inspecting the $response from a component that fires a download interestingly does have an effects part that contains download which in turn contains name and content, so it's possible to compare the name from the response with the expected download name.
Let's try this out!
Here's an export method of an existing component
The download is performed in this case by https://laravel-excel.com/ the export expects 2 params the format and optionally a timestamp.
public function export(string $format, \DateTime $timestamp = null): Bool|BinaryFileResponse
{
$params = [
'name' => $this->name,
'created_at' => $this->created_at
];
if ($timestamp === null) {
$timestamp = now();
}
if ($format === 'xlsx') {
return (new CustomersExport($params))->download("customers-$timestamp.xlsx");
}
if ($format === 'csv') {
return (new CustomersExport($params))->download("customers-$timestamp.csv", Excel::CSV);
}
if ($format === 'pdf') {
return (new CustomersExport($params))->download("customers-$timestamp.pdf", Excel::DOMPDF);
}
return true;
}
Now in the test assign $response to the component call the export method and pass in the format (xlsx) and the timestamp.
Next set the expected filename and then the actual bu reading the response's payload and looking inside ['effects']['download']['name'] this will return the name from the download.
Then you can assert the expected and actual are identical.
/** @test **/
public function can_export_xlsx(): void
{
$timestamp = now();
$response = Livewire::test(Customers::class)
->call('export', 'xlsx', $timestamp)
->assertStatus(200);
$expected = "customers-$timestamp.xlsx";
$actual = $response->payload['effects']['download']['name'];
self::assertSame($expected, $actual);
}
There may be a more direct way to test the download from Laravel Livewire but this appears like a good solution, if the component returns anything other then a 200 response the test will fail likewise if the response of the download does not match the expected filename the test will fail.