Dusk Version
8.3.3
Laravel Version
12.19.3
PHP Version
8.4
PHPUnit Version
11.5.25
Database Driver & Version
No response
Description
Description
When writing Dusk tests following the official documentation, IDE support breaks when chaining page methods after Browser::visit(Page).
Example with Page:
$this->browse(function (Browser $browser) {
$browser->visit(new PlaylistPage)
->createPlaylist('My Playlist')
->assertSee('My Playlist');
});
Example with Component:
$browser
->visit('/') // or ->visit(new AwesomePage)
/** @var Browser|DatePicker $datePicker */
->within(new DatePicker, function ($datePicker) {
$datePicker->selectDate(2019, 1, 30);
})
->assertSee('January');
// Or:
$page = $browser->visit('/'); // or ->visit(new AwesomePage)
/** @var Browser|DatePicker $datePicker */
$datePicker = $page->component(new DatePicker);
$datePicker
->selectDate(2019, 1, 30)
->assertSee('January');
Issue:
-
Browser::visit() returns $this (Browser).
-
The magic __call correctly dispatches the method call to the Page class at runtime.
-
However, the return type remains Browser, so the IDE does not recognize the page methods, nor does it know that createPlaylist() returns a Browser or PlaylistPage.
-
Similarly, Browser::with() works with Components but the __call magic is used to execute component methods at runtime.
This makes the fluent chaining work at runtime but breaks static analysis and autocompletion.
Proposed solution:
One possible approach would be to provide a way for Page and Component classes to receive the Browser instance and return it explicitly. For example:
// Page method
public function createPlaylist(string $name): Browser
{
return $this->browser
->type('name', $name)
->check('share')
->press('Create Playlist');
}
And in the Browser class:
if ($this->page && method_exists($this->page, $method)) {
$this->page->setBrowser($this);
return $this->page->{$method}(...$parameters);
}
This would allow tests to be written with proper type hints:
$this->browse(function (Browser $browser) {
/** @var PlaylistPage|Browser $playlistPage */
$playlistPage = $browser->visit(new PlaylistPage);
$playlistPage
->createPlaylist('My Playlist')
->assertSee('My Playlist');
});
Clarification
Alternatively, an official pattern for fluent type resolution or static return types for Page methods would solve the problem for IDEs and static analyzers.
I understand this would require changes to Browser, Page, and Component contracts. However, having proper static type resolution would make writing and verifying new tests slightly easier — at least for me! 😄
Steps To Reproduce
- Create a Page class with a method
createPlaylist(Browser $browser, string $name).
- In your Dusk test, call
$browser->visit(new PlaylistPage)->createPlaylist('My Playlist').
- Observe that the IDE cannot find
createPlaylist() because Browser::visit() returns Browser.
- The magic
__call works at runtime, but static analysis fails.
Dusk Version
8.3.3
Laravel Version
12.19.3
PHP Version
8.4
PHPUnit Version
11.5.25
Database Driver & Version
No response
Description
Description
When writing Dusk tests following the official documentation, IDE support breaks when chaining page methods after Browser::visit(Page).
Example with Page:
Example with Component:
Issue:
Browser::visit()returns$this(Browser).The magic
__callcorrectly dispatches the method call to the Page class at runtime.However, the return type remains Browser, so the IDE does not recognize the page methods, nor does it know that
createPlaylist()returns aBrowserorPlaylistPage.Similarly,
Browser::with()works with Components but the__callmagic is used to execute component methods at runtime.This makes the fluent chaining work at runtime but breaks static analysis and autocompletion.
Proposed solution:
One possible approach would be to provide a way for Page and Component classes to receive the Browser instance and return it explicitly. For example:
And in the Browser class:
This would allow tests to be written with proper type hints:
Clarification
Alternatively, an official pattern for fluent type resolution or static return types for Page methods would solve the problem for IDEs and static analyzers.
I understand this would require changes to Browser, Page, and Component contracts. However, having proper static type resolution would make writing and verifying new tests slightly easier — at least for me! 😄
Steps To Reproduce
createPlaylist(Browser $browser, string $name).$browser->visit(new PlaylistPage)->createPlaylist('My Playlist').createPlaylist()becauseBrowser::visit()returnsBrowser.__callworks at runtime, but static analysis fails.