Skip to content

Testing ​

INFO

This page documents the new Testing API. If you use the legacy one, please refer to Testing (legacy).

Sharp provides a fluent testing API to help you test your Sharp code. These assertions and helpers are designed to be used in Feature tests.

The SharpAssertions trait ​

To use Sharp's testing helpers, include the Code16\Sharp\Utils\Testing\SharpAssertions trait in your TestCase class:

php
use Code16\Sharp\Utils\Testing\SharpAssertions;

abstract class TestCase extends BaseTestCase
{
    use SharpAssertions;
    
    // ...
}

or in Pest.php:

php
use Code16\Sharp\Utils\Testing\SharpAssertions;

pest()
    ->extend(\Tests\TestCase::class)
    ->use(SharpAssertions::class);

Authentication ​

loginAsSharpUser ​

Sharp provides a helper to log in a user. By default, it will use the SharpAssertions internal logic to ensure the user is authorized to access Sharp.

php
it('allows the user to access the list', function () {
    $user = User::factory()->create();

    $this
        ->loginAsSharpUser($user)
        ->sharpList(Post::class)
        ->get()
        ->assertOk();
});

Testing Entity Lists ​

Use sharpList() to test your Entity Lists.

sharpList ​

Starts a fluent interaction with an Entity List.

php
$this->sharpList(Post::class)
    ->get()
    ->assertOk()
    ->assertListData(fn (AssertableJson $data) => $data
        ->count(3)
        ->has('0.title', 'My first post')
        ->etc()
    );

Filtering the list ​

You can use withFilter() to apply filters to the list before calling get() or a command.

php
$this->sharpList(Post::class)
    ->withFilter(CategoryFilter::class, 1)
    ->get()
    ->assertOk();

Entity Commands ​

You can call an Entity Command directly from the list:

php
$this->sharpList(Post::class)
    ->entityCommand(ExportPosts::class)
    ->post()
    ->assertOk()
    ->assertReturnsDownload('posts.csv');

If the command has a form, you can test it:

php
$this->sharpList(Post::class)
    ->entityCommand(ExportPosts::class)
    ->getForm()
    ->assertFormData(fn (AssertableJson $data) => $data
        ->where('format', 'xls')
        ->etc()
    )
    ->post(['format' => 'csv'])
    ->assertOk();

Instance Commands ​

Similarly, you can call an Instance Command:

php
$this->sharpList(Post::class)
    ->instanceCommand(PublishPost::class, 1)
    ->post()
    ->assertOk()
    ->assertReturnsReload();

Asserting Command results ​

The object returned by post() provides dedicated assertions for each Command return type. All parameters are optional: they are only checked when given.

  • assertReturnsInfo(?string $message = null, ?bool $reload = null)
  • assertReturnsLink(?string $url = null, ?bool $newTab = null)
  • assertReturnsReload()
  • assertReturnsRefresh(?array $ids = null)
  • assertReturnsView(?string $view = null, ?array $data = null)
  • assertReturnsStep(?string $step = null)
  • assertReturnsDownload(?string $filename = null, ?string $content = null), for both download() and streamDownload()
php
$this->sharpList(Post::class)
    ->entityCommand(InviteUsers::class)
    ->post()
    ->assertReturnsInfo('Invitation sent!', reload: true);

$this->sharpList(Post::class)
    ->entityCommand(ExportPosts::class)
    ->post()
    ->assertReturnsDownload('posts.csv', content: "id,title\n1,My first post\n");

Every TestResponse method is also available, and methods returning a value (like json()) give it back. This is useful to test validation and authorization:

php
$this->sharpList(Post::class)
    ->entityCommand(InviteUsers::class)
    ->post(['email' => ''])
    ->assertJsonValidationErrors(['email']);

$this->sharpList(Post::class)
    ->instanceCommand(PublishPost::class, 1)
    ->post()
    ->assertForbidden();

Multi-step Commands (Wizards) ​

For commands that have multiple steps, you can use getNextStepForm():

php
$this->sharpList(Post::class)
    ->entityCommand(MyWizardCommand::class)
    ->getForm()
    ->post(['step1_data' => 'value'])
    ->assertReturnsStep('step2')
    ->getNextStepForm()
    ->assertFormData(fn (AssertableJson $data) => $data
        ->where('step2_field', 'default')
        ->etc()
    )
    ->post(['step2_data' => 'value'])
    ->assertOk();

Entity States ​

Use entityState() to update the state of an instance. Since an Entity State has no form, the request is posted right away, and the same assertReturns*() assertions are available:

php
$this->sharpList(Post::class)
    ->entityState(1, 'published')
    ->assertOk()
    ->assertReturnsRefresh([1]);

Deleting an instance ​

php
$this->sharpList(Post::class)
    ->delete(1)
    ->assertOk();

Testing Show Pages ​

Use sharpShow() to test your Show Pages.

sharpShow ​

Starts a fluent interaction with a Show Page.

php
$this->sharpShow(Post::class, 1)
    ->get()
    ->assertOk()
    ->assertShowData(fn (AssertableJson $data) => $data
        ->where('title', 'My first post')
        ->where('author', 'John Doe')
        ->etc()
    );

Instance Commands from Show ​

php
$this->sharpShow(Post::class, 1)
    ->instanceCommand(PublishPost::class)
    ->post()
    ->assertOk();

Entity States from Show ​

php
$this->sharpShow(Post::class, 1)
    ->entityState('published')
    ->assertReturnsRefresh([1]);

// In a Single Show
$this->sharpShow(Profile::class)
    ->entityState('active')
    ->assertReturnsReload();

Deleting an instance ​

php
$this->sharpShow(Post::class, 1)
    ->delete()
    ->assertRedirect();

List & dashboard fields ​

Show Pages can contain embedded Entity Lists or Dashboards. You can test them using sharpListField() and sharpDashboardField().

sharpListField ​

php
$this->sharpShow(Post::class, 1)
    ->sharpListField(Comment::class)
    ->get()
    ->assertOk()
    ->assertListData(fn (AssertableJson $data) => $data
        ->count(5)
    );

sharpDashboardField ​

php
$this->sharpShow(User::class, 1)
    ->sharpDashboardField(UserStatsDashboard::class)
    ->get()
    ->assertOk();

Nested shows ​

There are some cases where you have nested shows by navigating through Show List fields. You can chain sharpShow() calls to simulate the correct breadcrumb :

php
$this->sharpList(Post::class)
    ->sharpShow(Post::class, 1)
    ->sharpListField(Comment::class)
    ->sharpShow(Comment::class, 1)
    ->get()
    ->assertOk();

Delete the instance ​

To test the deletion of the show instance, you can use delete();

php
$this->sharpShow(Post::class, 1)
    ->delete()
    ->assertRedirect();

Testing Forms ​

Use sharpForm() to test your Forms.

sharpForm ​

Starts a fluent interaction with a Form. If $instanceId is provided, it targets an edit form; otherwise, it targets a creation form.

Creating and Updating ​

php
// Create
$this->sharpForm(Post::class)
    ->store(['title' => 'New Post'])
    ->assertValid()
    ->assertRedirect();

// Update
$this->sharpForm(Post::class, 1)
    ->update(['title' => 'Updated Post'])
    ->assertValid()
    ->assertRedirect();

Testing the "Creation" or "Edit" request itself ​

If you want to test that the form displays correctly:

php
$this->sharpForm(Post::class, 1)
    ->edit()
    ->assertOk()
    ->assertFormData(fn (AssertableJson $data) => $data
        ->where('title', 'Existing Post')
        ->etc()
    );

From an AssertableForm (the result of edit() or create()), you can also call update() or store():

php
$this->sharpForm(Post::class, 1)
    ->edit()
    ->update(['title' => 'New title'])
    ->assertValid();

Testing Dashboards ​

Use sharpDashboard() to test your Dashboards.

sharpDashboard ​

Starts a fluent interaction with a Dashboard.

php
$this->sharpDashboard(MyDashboard::class)
    ->get()
    ->assertOk();

Filtering the dashboard ​

php
$this->sharpDashboard(MyDashboard::class)
    ->withFilter(PeriodFilter::class, ['start' => '2023-01-01', 'end' => '2023-01-31'])
    ->get()
    ->assertOk();

Dashboard Commands ​

php
$this->sharpDashboard(MyDashboard::class)
    ->dashboardCommand(RefreshStats::class)
    ->post()
    ->assertOk();

Remember that in a Dashboard, a refresh() return action is treated as a reload(): assert it with assertReturnsReload().

Global filters ​

If your app contains global filters, you should be able to test normally, but it will be set to its default value. If you need, you can set a specific value using withSharpGlobalFilter():

php
$this->withSharpGlobalFilter(CompanyFilter::class, 'apple')
    ->sharpList(Post::class)
    //...

Released under the MIT License.