BETA We're building something new — all help is welcome! Contribute →

Testing

Test your components server-side without a browser using a fluent API.

Basic Usage

Use LiVue::test() to create a testable component instance:

use App\LiVue\Counter;
use LiVue\Facades\LiVue;

class CounterTest extends TestCase
{
    public function test_counter_starts_at_zero()
    {
        LiVue::test(Counter::class)
            ->assertSet('count', 0);
    }

    public function test_counter_increments()
    {
        LiVue::test(Counter::class)
            ->call('increment')
            ->call('increment')
            ->assertSet('count', 2);
    }
}

Interaction Methods

Method Description
set('name', 'value') Simulate v-model update
set(['name' => 'value']) Batch property updates
call('method', $params) Call a component method
toggle('property') Toggle a boolean property
refresh() Re-render without changes
dispatch('event', $data) Dispatch an event to listeners

Assertions

Property Assertions

->assertSet('count', 5)
->assertSet('user.name', 'Mario')  // dot notation
->assertNotSet('status', 'error')
->assertCount('items', 3)

HTML Assertions

->assertSee('Counter: 5')
->assertDontSee('Error')
->assertSeeHtml('<strong>5</strong>')
->assertSeeInOrder(['Step 1', 'Step 2', 'Step 3'])

Validation Assertions

->assertHasErrors()                         // any errors
->assertHasErrors('email')                   // specific field
->assertHasErrors(['name', 'email'])         // multiple fields
->assertHasNoErrors()
->assertHasNoErrors(['name', 'email'])

Navigation Assertions

->assertRedirect()                          // any redirect
->assertRedirect('/dashboard')               // specific URL
->assertNoRedirect()
->assertNavigate('/users/1')                // SPA navigate

Event Assertions

->assertDispatched('form-saved')
->assertNotDispatched('error-occurred')

Full Example

class ContactFormTest extends TestCase
{
    public function test_validates_required_fields()
    {
        LiVue::test(ContactForm::class)
            ->call('submit')
            ->assertHasErrors(['name', 'email', 'message']);
    }

    public function test_validates_email_format()
    {
        LiVue::test(ContactForm::class)
            ->set('name', 'Mario')
            ->set('email', 'invalid-email')
            ->set('message', 'Hello World')
            ->call('submit')
            ->assertHasErrors('email')
            ->assertHasNoErrors(['name', 'message']);
    }

    public function test_submits_valid_form()
    {
        LiVue::test(ContactForm::class)
            ->set([
                'name' => 'Mario Rossi',
                'email' => 'mario@example.com',
                'message' => 'Hello World!',
            ])
            ->call('submit')
            ->assertHasNoErrors()
            ->assertDispatched('form-submitted')
            ->assertSee('Thank you!');
    }
}

Testing with Mount Parameters

Pass parameters to the component's mount() method:

public function test_loads_user_on_mount()
{
    $user = User::factory()->create(['name' => 'Mario Rossi']);

    LiVue::test(UserProfile::class, ['userId' => $user->id])
        ->assertSet('user.name', 'Mario Rossi')
        ->assertSee('Mario Rossi');
}

Advanced Setup

// Test as authenticated user
LiVue::test(AdminPanel::class)
    ->actingAs($admin)
    ->assertSee('Admin Dashboard');

// Test with query parameters (#[Url])
LiVue::test(SearchPage::class)
    ->withQueryParams(['q' => 'laravel', 'page' => 2])
    ->assertSet('query', 'laravel')
    ->assertSet('page', 2);

// Test without lazy loading
LiVue::test(LazyWidget::class)
    ->withoutLazyLoading()
    ->assertSee('Widget Content');

Accessors

Access component internals for custom assertions:

$testable = LiVue::test(Counter::class);

// Get property value
$count = $testable->get('count');

// Get component instance
$component = $testable->instance();

// Get rendered HTML
$html = $testable->html();

// Get snapshot (state + memo)
$snapshot = $testable->snapshot();

// Get last response
$response = $testable->call('increment')->response();

How It Works

The testing system executes the full component lifecycle server-side, simulating what happens during real AJAX requests:

  1. 1. Component is instantiated and mount() is called
  2. 2. Initial render produces HTML and snapshot
  3. 3. Each set() or call() runs a full processUpdate() cycle
  4. 4. Assertions check the resulting state and HTML

This ensures tests exercise the exact same code path as production requests.