Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] Add assertDownloadOffered test method to TestResponse class #37532

Merged
merged 5 commits into from
Jun 1, 2021

Conversation

tanmaymishu
Copy link
Contributor

@tanmaymishu tanmaymishu commented May 29, 2021

This PR introduces a convenient assertDownloadOffered($filename = null) method which makes it easier to test different types of file responses such as:

\Response::download(Storage::get($path), 'image.png');
\Response::streamDownload(function() {
    return \Storage::get('download.png');
}, 'image.png');
\Storage::download($path, 'image.png');
new \Symfony\Component\HttpFoundation\BinaryFileResponse($path, 200, [], true, 'attachment');
\Response::make(Storage::get($path), 200, [
    'Content-Disposition' => 'attachment; filename=image.png',
]);

Unless explicitly overridden, most of these approaches add attachment as the disposition-type to the Content-Disposition header, which asks the HTTP client to download the file locally, as shown in the screenshot below:

image

In order to test whether a route/controller action responds with a downloadable file or not and whether that file is indeed the correct one, currently one has to extract the header, split it and inspect the content in a hacky way. This kind of approach lacks chainability as well.

The proposed TestResponse::assertDownloadOffered() method ensures two things:

  1. The file is downloaded ($response->assertDownloadOffered())
  2. The file with the expected name is downloaded ($response->assertDownloadOffered('image.png'))

Intent Resolution

As shown in the examples above, as long as the disposition-type parameter of the Content-Disposition header is set to attachment, it will be determined that the intention is to download the file. Hence, in cases where disposition-type is changed to something else like this:

\Response::download(Storage::get($path), 'image.png', [], 'inline');

...even though the called method is download(), replacing attachment with inline instructs the HTTP client to display the file instead of downloading. In such cases, $response->assertDownloadOffered() will mark the test as failed.

Examples

  1. The test should pass when the specified file is sent from the response.
// Action:
Route::get('/', function () {
    return Response::download(Storage::path('download.png'), 'image.png');
});

// Test:
public function testExample()
{
    $this->get('/')->assertDownloadOffered('image.png')->assertOk();
}

// Result:
// OK (1 test, 2 assertions)
  1. The test should fail when a different file is sent than expected.
// Action:
Route::get('/', function () {
    return Response::download(Storage::path('download.png'), 'something_else.png');
});

// Test:
public function testExample()
{
    $this->get('/')->assertDownloadOffered('image.png')->assertOk();
}

// Result:
// Expected file [image.png] is not present in Content-Disposition header.
// Failed asserting that two strings are identical.
// Expected :'image.png'
// Actual   :'something_else.png'
  1. The test should pass even when the attachment option is manually passed to the header.
// Action:
Route::get('/', function () {
    return \Response::make(Storage::get('download.png'), 200, [
        'Content-Disposition' => 'attachment; filename=image.png',
    ]);
});

// Test:
public function testExample()
{
    $this->get('/')->assertDownloadOffered('image.png')->assertOk();
}

// Result:
// OK (1 test, 2 assertions)
  1. The test should fail when disposition is set to anything other than attachment.
// Action:
Route::get('/', function () {
    return \Response::download(Storage::get('download.png'), 'image.png', [], 'inline');
});

// Test:
public function testExample()
{
    $this->get('/')->assertDownloadOffered('image.png')->assertOk();
}

// Result:
// Response does not offer a file download.
// Disposition param [inline] found in header, [attachment] expected.
  1. The test should pass when the file is sent with a name but no name specified during assertion.
// Action:
Route::get('/', function () {
    return Response::download(Storage::path('download.png'), 'image.png');
});

// Test:
public function testExample()
{
    $this->get('/')->assertDownloadOffered()->assertOk();
}

// Result:
// OK (1 test, 2 assertions)

Compatibility

This PR does not include breaking changes.

@tanmaymishu tanmaymishu changed the title Add assertDownloadOffered() test method to \Illuminate\Testing\TestResponse class [8.x] Add assertDownloadOffered() test method to \Illuminate\Testing\TestResponse class May 29, 2021
@driesvints driesvints changed the title [8.x] Add assertDownloadOffered() test method to \Illuminate\Testing\TestResponse class [8.x] Add assertDownloadOffered test method to TestResponse class May 30, 2021
@taylorotwell taylorotwell merged commit 9e23c74 into laravel:8.x Jun 1, 2021
@tanmaymishu tanmaymishu deleted the file-response-assertions branch June 6, 2021 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants