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:
use Code16\Sharp\Utils\Testing\SharpAssertions;
abstract class TestCase extends BaseTestCase
{
use SharpAssertions;
// ...
}or in Pest.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.
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.
$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.
$this->sharpList(Post::class)
->withFilter(CategoryFilter::class, 1)
->get()
->assertOk();Entity Commands
You can call an Entity Command directly from the list:
$this->sharpList(Post::class)
->entityCommand(ExportPosts::class)
->post()
->assertOk()
->assertReturnsDownload('posts.csv');If the command has a form, you can test it:
$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:
$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 bothdownload()andstreamDownload()
$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:
$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():
$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:
$this->sharpList(Post::class)
->entityState(1, 'published')
->assertOk()
->assertReturnsRefresh([1]);Deleting an instance
$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.
$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
$this->sharpShow(Post::class, 1)
->instanceCommand(PublishPost::class)
->post()
->assertOk();Entity States from Show
$this->sharpShow(Post::class, 1)
->entityState('published')
->assertReturnsRefresh([1]);
// In a Single Show
$this->sharpShow(Profile::class)
->entityState('active')
->assertReturnsReload();Deleting an instance
$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
$this->sharpShow(Post::class, 1)
->sharpListField(Comment::class)
->get()
->assertOk()
->assertListData(fn (AssertableJson $data) => $data
->count(5)
);sharpDashboardField
$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 :
$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();
$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
// 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:
$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():
$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.
$this->sharpDashboard(MyDashboard::class)
->get()
->assertOk();Filtering the dashboard
$this->sharpDashboard(MyDashboard::class)
->withFilter(PeriodFilter::class, ['start' => '2023-01-01', 'end' => '2023-01-31'])
->get()
->assertOk();Dashboard Commands
$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():
$this->withSharpGlobalFilter(CompanyFilter::class, 'apple')
->sharpList(Post::class)
//...