Search code examples
laraveltestinghomesteadlaravel-dusklaravel-testing

Laravel testing url to resource in public directory


I'm using Laravel Dusk to test all links on a page, these links are PDF files that are that are copied from resources/docs to public/docs with laravel mix.
The problem is that trying to access the URL of the docs using get(uri) method gives a 404, here is a simplified version of my Dusk:

protected function refreshApplicationWithLocale($locale)
{
    self::tearDown();
    putenv(LaravelLocalization::ENV_ROUTE_KEY . '=' . $locale);
    self::setUp();
}

protected function setUp(): void
{
    parent::setUp();
    if(!File::copyDirectory( resource_path('docs'), public_path() . '/docs'))
        throw new \Exception("couldn't copy the folder :(");
    // Storage::assertExists(public_path() . '/docs/clothing/existing_file.pdf'); // this assertion fails, what's weird is the path in the exception message does actually exist
    $this->artisan('storage:link');
}

protected function tearDown(): void
{
    parent::tearDown();
    foreach (static::$browsers as $browser) {
        $browser->driver->manage()->deleteAllCookies();
    }
}

public function testClothing()
{
    $this->refreshApplicationWithLocale('fr');
    $this->get('/test')->assertStatus(200); // passes
    $this->get('/fr/vetements')->assertStatus(200); // passes
    //usually I'd get the page urls dynamically with Dusk, but for simplicity sake I'm just testing with this link that I know it exists and can curl it from withing Homestead
    $this->get('/docs/clothing/Hoodies.pdf')->assertStatus(200); //failes (404)
}

What am I missing ?
Thank you.
P.S.: I'm using laravel 8 and Homestead

EDIT:
After @steven7mwesigwa answer, here is my final code:

protected function refreshApplicationWithLocale($locale)
{
    self::tearDown();
    putenv(LaravelLocalization::ENV_ROUTE_KEY . '=' . $locale);
    self::setUp();
}

protected function setUp(): void
{
    parent::setUp();
    if(!File::copyDirectory( resource_path('docs'), public_path() . '/docs'))
        throw new \Exception("couldn't copy the documents folder :(");
}

protected function tearDown(): void
{
    parent::tearDown();
    foreach (static::$browsers as $browser) {
        $browser->driver->manage()->deleteAllCookies();
    }
}

public function testClothing()
{
    $this->refreshApplicationWithLocale('fr');
    $elements = [];
    $this->browse(function (Browser $browser) use(&$elements){
        $elements = $browser->visit('myroute')
            ->elements('.tmp_products_list a', 'href');
    });
    $this->assertGreaterThan(1, count($elements));
    foreach($elements as $element) {
        $response = Http::get($element->getAttribute('href'));
        self::assertEquals($response->status(), 200);
    }
}

Solution

  • Issue 1:

    // Storage::assertExists(public_path() . '/docs/clothing/existing_file.pdf');

    // this assertion fails, what's weird is the path in the exception message does actually exist

    The Storage facade uses the storage that is active in your config files (config/filesystems.php). Note that this defaults to local. The root path is set for each storage option. In the case of local, it's pointing to storage/app out of my head. - @bobbybouwmann

    In addition, the right way to search through the storage path, is to first specify the filesystem "disk". I.e, for a file residing in storage/app/public/demo.js:

    Storage::disk("public")->assertExists("demo.js");

    With that out of the way, since your desired test file(s) reside in the pubic folder, these could be tested using:

    $this->assertFileExists(public_path() . '/docs/clothing/existing_file.pdf');

    Issue 2:

    //usually I'd get the page urls dynamically with Dusk, but for simplicity sake I'm just testing with this link that I know it exists and can curl it from withing Homestead

    $this->get('/docs/clothing/Hoodies.pdf')->assertStatus(200); //failes (404)

    The above snippet of code doesn't work because $this->get(...) is essentially searching for an explicitly defined route in the application's route file(s) corresponding to this path which is not found.

    You must use a browser test framework (I.e Laravel Dusk) to test static files on a web server.

    More to that can be found here: How to test for static files located in the web folder in symfony with phpunit?

    Nonetheless, since you're using Laravel Dusk already, this can be achieved by:

            $this->browse(function (Browser $browser) {
                $browser->visit('/docs/clothing/Hoodies.pdf')
                    ->assertDontSee("404")
                    ->assertDontSee("NOT FOUND");
            });
    
    

    If the URL is valid and the PDF file exists and is loaded, no HTML view will be loaded. Hence the test should pass.

    If the URL is invalid, Laravel's default 404.blade.php view will be loaded. This view includes the following text strings: "404" and "NOT FOUND". Hence the test would fail in this case.

    Feel free to change the text strings passed in the assertDontSee(....) method parameter in case you already have a custom 404 error page.