yulquen's avatar

How to test response->download to download a PDF?

In a controller i got something like this

public function download($file)
{
    $download = $this->download->getFile($file);
    $path     = realpath(base_path('resources/downloads')) . $file;

    if (!file_exists($path)) {
        abort(404);
    }

    if (!$download->belongsToUser($this->user->id)) {
        abort(404);
    }

    return response()->download($path, $this->getFilename($file), ['content-type' => ' application/pdf']);
}

Right now my test looks something like this:

// ...

$this->actingAs($user1)
            ->visit('/downloads')
            ->seePageIs('/downloads')
            ->see($downloadUser1->name)
            ->dontSee($downloadUser2->name)
            ->click($downloadUser1->name)
            ->seeStatusCode(200);

But for this i got the following error:

InvalidArgumentException: Expecting a DOMNodeList or DOMNode instance, an array, a string, or null, but got "boolean".

I driving my head around how to test this :D Is there any possibility to test the download within an integration test?

0 likes
14 replies
sid405's avatar

Hey @rephluX

Where in the stack is that error being thrown? Can you post a bit more of the error log for this matter?

yulquen's avatar

Hey @sid405

Of course, here is full error trace thrown by PHPUnit:

InvalidArgumentException: Expecting a DOMNodeList or DOMNode instance, an array, a string, or null, but got "boolean".

/home/vagrant/code/laravel-demo/vendor/symfony/dom-crawler/Crawler.php:95
/home/vagrant/code/laravel-demo/vendor/symfony/dom-crawler/Crawler.php:59
/home/vagrant/code/laravel-demo/vendor/laravel/framework/src/Illuminate/Foundation/Testing/CrawlerTrait.php:188
/home/vagrant/code/laravel-demo/vendor/laravel/framework/src/Illuminate/Foundation/Testing/CrawlerTrait.php:66
/home/vagrant/code/laravel-demo/vendor/laravel/framework/src/Illuminate/Foundation/Testing/CrawlerTrait.php:862
/home/vagrant/code/laravel-demo/tests/integration/DownloadTest.php:32

The pdf file is present in the specified directory of course :-)

yulquen's avatar

@sid405

That would be nice! Thank you in advance :-)

I also dig in a bit deeper and as far as i can say it must be an issue in the Testing/CrawlerTrait.php:178

protected function makeRequest($method, $uri, $parameters = [], $cookies = [], $files = [])
{
    // ...
    $this->call($method, $uri, $parameters, $cookies, $files);

    $this->currentUri = $this->app->make('request')->fullUrl();

    $this->crawler = new Crawler($this->response->getContent(), $uri);

    return $this;
}

$this->response is type of Symfony\Component\HttpFoundation\BinaryFileResponse and getContent() returns false because the content property on that response object is NULL. That is why the error is thrown.

mabasic's avatar

I will try this also. Need to test the same thing. But I would probably check that headers match conatain content type application/pdf

1 like
sid405's avatar

@rephluX what is

 $download = $this->download->getFile($file);

Referring to? what do you have injected as 'download' in the class constructor?

sid405's avatar

@rephluX Ait so that's your download model i gather.

What's the $this->getFilename function like?

Other than the test failing, is the code working properly?

yulquen's avatar

@sid405

Yes, $download = $this->download->getFile($file) is getting the requested file through a $download a repository that is injected in the class constructor. So, it's not the model itself, but i think, for failing the unit test, that doesn't even matter ;-)

And $this->getFilename is just a sanitizing function, that appends a prefix to the requested file before prompting to the user for download that file.

And yes, the code is working as intended. And even when i unit test this, it still works. At least i get a BinaryFileResponse when i var_dump some of the objects/variables in the makeRequest method that i mentioned in my previous post.

But when the Crawler is being instantiated, the error is thrown because $node is a boolean and not a DomNode/String/Array. See Crawler.php:84 for that.

robmpreston's avatar
/** @test */
public function we_can_download_an_application_pdf()
{
    $user = factory(User::class)->create();
    $application = $this->makePartiallyCompletedApplication($user);
        
    $expected_pdf_name = 'Application-' . $application->id . '.pdf';
        
    $response = $this->actingAs($user)
                    ->call('GET', '/api/application/pdf/' . $user->token . '/' . $application->id);
    $this->assertTrue($response->headers->get('content-type') == 'application/pdf');
    $this->assertTrue($response->headers->get('content-disposition') == 'attachment; filename="' . $expected_pdf_name . '"');
}
6 likes
kulka1's avatar

@rephluX just saw this thread, i have actually the same problem. how did you solve it?

i want to test if the file was correctly downloaded after a form submit and assert it with an other test file.

simon1709's avatar

This solution works great except the fact that it has unnecessary pair of quotes. The line that works for me is

    $this->assertTrue($response->headers->get('content-disposition') == 'attachment; filename=' . $expected_pdf_name . '');
1 like
AM_Bolt's avatar

You can also use follow method for chek downloaded file content

$response->streamedContent();

Please or to participate in this conversation.