gnikyt / laravel-shopify

A full-featured Laravel package for aiding in Shopify App development
MIT License
1.24k stars 374 forks source link

Inertia.js is untestable with Laravel feature tests #1134

Closed grahamsutton closed 2 years ago

grahamsutton commented 2 years ago

For bug reporting only! If you're posting a feature request or discussion, please ignore.

Expected Behavior

I should be able to use $response->assertInertia(...) without getting an error.

Current Behavior

Currently I am trying to write a feature test that keeps resulting in a Not a valid Inertia response..

In the AuthController@token action where /authenticate/token goes to, it appears that the app renders its own view, and I believe this is interfering with the ability of my test to use $response->assertInertia(...) because it renders a completely different view before it even reaches the controller.

Initially, I tried to test this by making a request directly to the route, but this is counter-intuitive as it doesn't simulate the real scenario which requires authentication.

Steps to Reproduce

  1. Install fresh Laravel project
  2. Install Inertia.js
  3. Create a controller and route that should render an Inertia component.
  4. Create a feature tests and use $response->assertInertia(...) to assert the component gets rendered.
  5. Observe "Not a valid Inertia response." failure.

Context

Failure Logs

  • Tests\Feature\Boxes\ListBoxesTest > can list boxes
  Not a valid Inertia response.

  at vendor/inertiajs/inertia-laravel/src/Testing/AssertableInertia.php:34
     30▕             PHPUnit::assertArrayHasKey('props', $page);
     31▕             PHPUnit::assertArrayHasKey('url', $page);
     32▕             PHPUnit::assertArrayHasKey('version', $page);
     33▕         } catch (AssertionFailedError $e) {
  ➜  34▕             PHPUnit::fail('Not a valid Inertia response.');
     35▕         }
     36▕ 
     37▕         $instance = static::fromArray($page['props']);
     38▕         $instance->component = $page['component'];

      +2 vendor frames 
  3   tests/Feature/Boxes/ListBoxesTest.php:34
      Illuminate\Testing\TestResponse::__call()

  Tests:  1 failed
  Time:   0.13s

and my test looks like:


    use Inertia\Testing\AssertableInertia as Assert;
    ...

    /** @test */
    public function can_list_boxes()
    {
        $this->followingRedirects();

        $user = User::factory()
            ->hasBoxes(5)
            ->create([
                'email' => 'test.myshopify.com'
            ]);

        $path = url()->tokenRoute('boxes.index', ['shop' => 'test.myshopify.com']);

        $response = $this->actingAs($user)->get($path);

        $response->assertStatus(200);

        $response->assertInertia(fn (Assert $page) => $page
            ->component('Boxes/List')
            ->has('boxes', 5)
        );
    }

and the route in routes/web.php:

Route::middleware('verify.shopify')->group(function () {
    Route::get('/boxes', 'BoxesController@index')->name('boxes.index');
});
grahamsutton commented 2 years ago

Closing this issue in favor of #1141 which addresses the issue more generally.

sp-artisan commented 2 years ago

Hello, Have you faced any problems building shopify apps with this package and inertia both ? I am thinking to use inertia for my next project so asking.

grahamsutton commented 2 years ago

@sahilcrawlapps Overall, it's a great package, so I still recommend using it. However, one of the drawbacks I've had is doing testing with Inertia.js in Laravel. The reason is that this package loads a page to authenticate itself, and that page is always loaded first, even before your Inertia.js file/component. So if you try to assert that Inertia loaded a page correctly, it's always going to assert it against the first page that gets loaded and that's where it becomes nearly impossible to get around.

I've instead moved away from using Inertia.js and instead am just using plain old React with a plain old Laravel API and it works fine. For my feature tests I just do $this->actingAs($user) as normal prior to sending a request and I also removed the middleware from being applied to my tests in tests/TestCase.php.

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Osiset\ShopifyApp\Http\Middleware\VerifyShopify;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;

    /**
     * Skip middleware that attempts to authenticate shop by JWT.
     *
     * Use $this->actingAs(\App\Models\User) to perform authenticated requests.
     *
     * @return void
     */
    public function setUp(): void
    {
        parent::setUp();

        $this->withoutMiddleware(VerifyShopify::class);
    }
}

Ideally, I would love to test everything, including the VerifyShopify middleware to simulate a request as authentically as possible, but this has been a pretty acceptable trade-off that I can live with.