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

Flux components automatically fails in phpunit #704

Open
cstisa opened this issue Nov 20, 2024 · 11 comments
Open

Flux components automatically fails in phpunit #704

cstisa opened this issue Nov 20, 2024 · 11 comments

Comments

@cstisa
Copy link

cstisa commented Nov 20, 2024

Hi,

I just encountered the problem while doing some tests. For some reason, when a Flux component is in my livewire blade template, it triggers an error whille running the tests.

Step to reproduce :

php artisan make:livewire create-post --test

then in create-post-blade.php :

<div>
    {{-- The whole world belongs to you. --}}
    <flux:button>Click</flux:button>
</div>

then run : php artisan test

I get this error :
Illuminate\View\ViewException: Call to a member function __call() on null (View: /home/panel/panel/vendor/livewire/flux/stubs/resources/views/flux/button/index.blade.php) (View: /home/panel/panel/vendor/livewire/flux/stubs/resources/views/flux/button/index.blade.php) (View: /home/panel/panel/vendor/livewire/flux/stubs/resources/views/flux/button/index.blade.php)

But I get no error in while loading the page with the component.

this is my phpunit.xml :

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         colors="true"
>
    <testsuites>
        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>
    </testsuites>
    <source>
        <include>
            <directory suffix=".php">./app</directory>
        </include>
    </source>

    <php>
        <env name="APP_ENV" value="testing"/>
    </php>

</phpunit>

Any help with this? I can't find anywhere something similar.

Note : Removing the Flux button allow my test to pass

@jeffchown
Copy link

@cstisa Can you show the actual test file that is triggering the error?

@cstisa
Copy link
Author

cstisa commented Nov 20, 2024

class CreatePostTest extends TestCase
{
  /** @test */
  public function renders_successfully()
  {
    Livewire::test(CreatePost::class)
      ->assertStatus(200);
  }
}

like I said, it is the file generated from the command line I pastedd earlier.

I'm starting to ger crazy because I have no clue where to look for.

@jeffchown
Copy link

jeffchown commented Nov 20, 2024

@cstisa I ran php artisan make:livewire create-post --test and tried the blade file with and without the <flux:button>... but could not reproduce your error. php artisan test runs successfully for me both ways.

Have you just started using Flux? Did you follow all of the installation instructions?:
https://fluxui.dev/docs/installation

@cstisa
Copy link
Author

cstisa commented Nov 21, 2024

Yes @jeffchown , I installed everything properly. And every thing works fine whille navigating on my website. This happens only when testing. I'm trying to investigated further with my project. I'll let you know if I find something.

@jeffchown
Copy link

jeffchown commented Nov 21, 2024

@cstisa Did you publish any of the Flux components (e.g. flux:button) via php artisan flux:publish?

Also, can you confirm it is the CreatePostTest that is causing the error by testing only that file via php artisan test --filter CreatePostTest?

Have you made any changes to your tests/TestCase.php?

Working the problem via process of elimination...

@cstisa
Copy link
Author

cstisa commented Nov 21, 2024

I didn't published any Flux components.
I didn't change TestCase either :

<?php

namespace Tests;

use Illuminate\Contracts\Console\Kernel;
use Illuminate\Foundation\Application;

/**
 * Trait CreatesApplication
 *
 * Creates the application instance.
 */
trait CreatesApplication
{
    /**
     * Creates the application.
     */
    public function createApplication(): Application
    {
        $app = require __DIR__.'/../bootstrap/app.php';

        $app->make(Kernel::class)->bootstrap();

        return $app;
    }

    /**
     * Asserts that the application is in a bootstrapped state.
     */
    protected function assertApplicationBootstrapped()
    {
        $this->assertTrue($this->app->hasBeenBootstrapped());
    }
}

Also, testing via php php artisan test --filter CreatePostTest, or just php artisan test is the same result.

I also deletedd every other test files on my project just to test this one CreatePostTest.php file and still the same error.

The only thing is that the error started when I did a (simple) merge with another branch of my git project.

It's been too many hours to diagnose the problem, so I decided to start from a commit where everything worked, and I'll add up again one change at a time to determine what's the root cause.

@jeffchown
Copy link

@cstisa Sounds like a good plan. Good luck! 🤞

@cstisa
Copy link
Author

cstisa commented Nov 21, 2024

I found the origin.

I'm using Fortify and I wanted to test the UpdateUserPassword class, and especially the update function.

<?php

namespace App\Actions\Fortify;

use Flux\Flux;
use Illuminate\Foundation\Auth\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\UpdatesUserPasswords;

/**
 * Fortify update password class
 */
class UpdateUserPassword implements UpdatesUserPasswords
{
  use PasswordValidationRules;

  /**
   * Validate and update the user's password.
   *
   * @param  array<string, string>  $input
   */
  public function update(User $user, array $input): void
  {
    Validator::make($input, [
      'current_password' => ['required', 'string', 'current_password:web'],
      'password' => $this->passwordRules(),
    ], [
      'current_password.current_password' => __('auth.wrong_current_password'),
    ])->validateWithBag('updatePassword');

    $user->forceFill([
      'password' => Hash::make($input['password']),
    ])->save();

    Flux::toast(
      text: __('general/confirmations.profilUpdateDone', ['entity' => __('model/user.password')]),
      variant: 'success'
    );
  }
}

This was my test :

  /** @test */
  public function it_updates_the_password_successfully()
  {
    $user = User::factory()->create([
      'password' => Hash::make('Csti010623!Csti010623!test#'),
    ]);

    $input = [
      'current_password' => 'OldPassword',
      'password' => 'NewPassword',
      'password_confirmation' => 'NewPassword',
    ];

    // Mock authentication
    $this->be($user); // Simulates logging in the user

    // Mock Flux::toast
    $fluxMock = Mockery::mock('alias:' . Flux::class);
    $fluxMock->shouldReceive('toast')
      ->once()
      ->withArgs(function ($text, $variant) {
        return $text === __('general/confirmations.profilUpdateDone', ['entity' => __('model/user.password')])
          && $variant === 'success';
      })
      ->andReturnNull();

    // Run the action
    $this->action->update($user, $input);

    // Assert that the password was updated
    $this->assertTrue(Hash::check($input['password'], $user->fresh()->password));
  }

I was mocking the Flux class because otherwise, I would get the following error when doing the Flux::toast :

Error: Call to a member function dispatch() on false

But, when I mocked Flux, the error would disappear and my whole test would success.
However, for some reason, it would obliterate all of my other tests with Flux components.

To conclude, I suppose I have to remove the mocking, but I don't know how to assert that update method indeed triggers the Toast, and not having an error while trying to assert it.

@jeffchown do you have any idea on how to solve my problem please?

I'd like to specify that the component calling the update method is a classic Laravel component and not a Livewire component

@jeffchown
Copy link

jeffchown commented Nov 21, 2024

@cstisa That's an important piece of information re: you mocking Flux.
Mocking can cause unexpected results - as seen in many Stackoverflow posts re: the error you received.

As for testing toast, first, I don't think Flux::toast can be easily called from outside a Livewire component the way you are calling it.
(there is a discussion on this with a potential solution: #505 (comment))

Second, if you are calling Toast from within a Livewire component, you should be able to test it via something like this:

Livewire::test(componentName::class)
   ...Arrange, Act...
    ->assertDispatched('toast-show');

(you can see how toast is shown in vendor/livewire/flux/src/Concerns/InteractsWithComponents.php
app('livewire')->current()->dispatch('toast-show', ...$params);)

Past this, I'll have to defer to the expertise of @calebporzio and @joshhanley

@cstisa
Copy link
Author

cstisa commented Nov 21, 2024

Great, I was worried about my level of comprehension of Laravel, Livewire and Flux, but at least, I new about :

Livewire::test(componentName::class)
   ...Arrange, Act...
    ->assertDispatched('toast-show');

I suppose I won't test the dispatch for this specific case for now.

Thanks again @jeffchown and I'll be watching this specific test case in the future 😅

@jeffchown
Copy link

You're welcome, @cstisa

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

No branches or pull requests

2 participants