phplaravellaravel-livewirefilamentphpphp-pest

Testing related fields in filament and livewire


I'm trying to test a form that when I select a brand it should prefill the related suppliers in the next select. In my form I user the ->live() method to make the fields dependent on each other. In my test i have:

$brand = Brand::factory()->create();
$suppliers = Supplier::factory()
    ->count(2)
    ->hasAttached($brand)
    ->create();

$this->get(OrderResource::getUrl('create'));
livewire(CreateOrder::class)
    ->assertFormFieldExists('brand_id')
    ->fillForm([
        'brand_id' => $brand->id,
    ])
    ->assertFormSet(['supplier_id' => $suppliers->pluck('id')->toArray()]);

However this doesn't work, I get the error:

Failed asserting that null matches expected 1.

  at vendor/livewire/livewire/src/Features/SupportTesting/MakesAssertions.php:93
     89▕
     90▕         if (! is_string($value) && is_callable($value)) {
     91▕             PHPUnit::assertTrue($value($actual));
     92▕         } else {
  ➜  93▕             $strict ? PHPUnit::assertSame($value, $actual) : PHPUnit::assertEquals($value, $actual);
     94▕         }
     95▕
     96▕         return $this;
     97▕     }

I have tried to log what the value is of the suppliers. And I have confirmed that the options are empty. How Can I solve this, or where can I read about testing these dependent fields. Thanks.


Solution

  • You didn t show the form but it must be similar to the one below (the example form below is the one shown here:

    class CreateOrder extends Component implements HasForms
    {
    use InteractsWithForms;
    
    public $brandId;
    public $supplierId;
    
    public function getFormSchema(): array
    {
      return [
    
        Select::make('brandId')
        ->label('Brands')
        ->options(Brand::all()->pluck('name','id')->toArray())
        ->reactive(),
     
        Select::make('supplierId')
        ->label('Suppliers')
        ->options(function(callable $get){
            $brand = Brand::find($get('brandId'));
         
            if(!$brand){
              return Supplier::all()->pluck('title','id');
            }
            return $brand->suppliers->pluck('title','id');
        })
    
      ];
    }
    
    
    public function render():View
    {
        return view('livewire.createorder');
    }
    }
    

    The form has 2 dependant selects, the second one lists all suppliers in the database if no brand is chosen in the first select, but if a brand is chosen it re-renders to list only the suppliers that belongs to the chosen brand.

    Below is the test:

    it('has dependant fields with logic ok', function () {
    
    $brand = Brand::factory()->create();
    $brand2 = Brand::factory()->create();
    
    $suppliersVisible = Supplier::factory()
        ->count(2)->create(['user_id' => $brand->id, 'title'=>fake()->sentence()]);
    
    $suppliersHidden = Supplier::factory()
        ->count(2)->create(['user_id' => $brand2->id,'title'=>fake()->sentence()]);
    
    livewire(CreateOrder::class)
        ->fillForm([
            'brandId' => $brand->id,
        ])
        ->assertSee($suppliersVisible[0]->title)
        ->assertSee($suppliersVisible[1]->title)
        ->assertDontSee($suppliersHidden[0]->title)
        ->assertDontSee($suppliersHidden[1]->title);
    });
    

    The test populate the db with 2 brands and each brand has 2 suppliers.

    The test passes if only the suppliers for the selected brand are shown after the re-render.

    The assertFormSet method is to test state of the form, but in this case there is no state, we have to test what the page shows.