Skip to content

Commit 4c01026

Browse files
committed
feat: add object page support to visit()
1 parent 4501dd4 commit 4c01026

18 files changed

+571
-58
lines changed

src/Api/AwaitableWebpage.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Pest\Browser\Exceptions\BrowserExpectationFailedException;
88
use Pest\Browser\Execution;
9+
use Pest\Browser\Page as BrowserPage;
910
use Pest\Browser\Playwright\Page;
1011
use Pest\Browser\Playwright\Playwright;
1112
use Pest\Browser\ServerManager;
@@ -24,7 +25,7 @@
2425
*/
2526
public function __construct(
2627
private Page $page,
27-
private string $initialUrl,
28+
private string|BrowserPage $initialUrl,
2829
private array $nonAwaitableMethods = [
2930
'assertScreenshotMatches',
3031
'assertNoAccessibilityIssues',

src/Api/Concerns/InteractsWithToolbar.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Pest\Browser\Api\Concerns;
66

77
use Pest\Browser\Api\Webpage;
8+
use Pest\Browser\Page as BrowserPage;
89
use Pest\Browser\Support\ComputeUrl;
910

1011
/**
@@ -27,8 +28,10 @@ public function refresh(): self
2728
*
2829
* @param array<string, mixed> $options
2930
*/
30-
public function navigate(string $url, array $options = []): self
31+
public function navigate(string|BrowserPage $url, array $options = []): self
3132
{
33+
$this->page->setShorthandElements($url);
34+
3235
$url = ComputeUrl::from($url);
3336

3437
$this->page->goto($url, $options);

src/Api/Concerns/MakesConsoleAssertions.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public function assertNoBrokenImages(): Webpage
3939

4040
expect($brokenImages)->toBeEmpty(sprintf(
4141
'Expected no broken images on the page initially with the url [%s], but found %s: %s',
42-
$this->initialUrl,
42+
$this->initialUrl(),
4343
count($brokenImages),
4444
implode(', ', $brokenImages),
4545
));
@@ -64,7 +64,7 @@ public function assertNoConsoleLogs(): Webpage
6464

6565
expect($consoleLogs)->toBeEmpty(sprintf(
6666
'Expected no console logs on the page initially with the url [%s], but found %s: %s',
67-
$this->initialUrl,
67+
$this->initialUrl(),
6868
count($consoleLogs),
6969
implode(', ', array_map(fn (array $log) => $log['message'], $consoleLogs)),
7070
));
@@ -81,7 +81,7 @@ public function assertNoJavaScriptErrors(): Webpage
8181

8282
expect($javaScriptErrors)->toBeEmpty(sprintf(
8383
'Expected no JavaScript errors on the page initially with the url [%s], but found %s: %s',
84-
$this->initialUrl,
84+
$this->initialUrl(),
8585
count($javaScriptErrors),
8686
implode(', ', array_map(fn (array $log) => $log['message'], $javaScriptErrors)),
8787
));

src/Api/Concerns/MakesElementAssertions.php

Lines changed: 37 additions & 37 deletions
Large diffs are not rendered by default.

src/Api/From.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Pest\Browser\Enums\BrowserType;
99
use Pest\Browser\Enums\Cities;
1010
use Pest\Browser\Enums\Device;
11+
use Pest\Browser\Page as BrowserPage;
1112

1213
/**
1314
* @mixin PendingAwaitablePage
@@ -22,7 +23,7 @@
2223
public function __construct(
2324
private BrowserType $browserType,
2425
private Device $device,
25-
private string $url,
26+
private string|BrowserPage $url,
2627
private array $options,
2728
) {
2829
//

src/Api/On.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Pest\Browser\Enums\BrowserType;
88
use Pest\Browser\Enums\Device;
9+
use Pest\Browser\Page as BrowserPage;
910

1011
/**
1112
* @mixin PendingAwaitablePage
@@ -20,7 +21,7 @@
2021
public function __construct(
2122
private BrowserType $browserType,
2223
private Device $device,
23-
private string $url,
24+
private string|BrowserPage $url,
2425
private array $options,
2526
) {
2627
//

src/Api/PendingAwaitablePage.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Pest\Browser\Enums\Cities;
1010
use Pest\Browser\Enums\ColorScheme;
1111
use Pest\Browser\Enums\Device;
12+
use Pest\Browser\Page as BrowserPage;
1213
use Pest\Browser\Playwright\InitScript;
1314
use Pest\Browser\Playwright\Playwright;
1415
use Pest\Browser\Support\ComputeUrl;
@@ -31,7 +32,7 @@ final class PendingAwaitablePage
3132
public function __construct(
3233
private readonly BrowserType $browserType,
3334
private readonly Device $device,
34-
private readonly string $url,
35+
private readonly string|BrowserPage $url,
3536
private readonly array $options,
3637
) {
3738
//
@@ -177,8 +178,8 @@ private function createAwaitablePage(): AwaitableWebpage
177178
$url = ComputeUrl::from($this->url);
178179

179180
return new AwaitableWebpage(
180-
$context->newPage()->goto($url, $this->options),
181-
$url,
181+
$context->newPage()->setShorthandElements($this->url)->goto($url, $this->options),
182+
$this->url,
182183
);
183184
}
184185
}

src/Api/Webpage.php

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
namespace Pest\Browser\Api;
66

7+
use BadMethodCallException;
78
use Pest\Browser\Execution;
9+
use Pest\Browser\Page as BrowserPage;
810
use Pest\Browser\Playwright\Locator;
911
use Pest\Browser\Playwright\Page;
1012
use Pest\Browser\Support\GuessLocator;
@@ -27,11 +29,32 @@
2729
*/
2830
public function __construct(
2931
private Page $page,
30-
private string $initialUrl,
32+
private string|BrowserPage $initialUrl,
3133
) {
3234
//
3335
}
3436

37+
/**
38+
* Dynamically call a method on the browser.
39+
*
40+
* @param array<int, mixed> $arguments
41+
*
42+
* @throws BadMethodCallException
43+
*/
44+
public function __call(string $method, array $arguments): self
45+
{
46+
if ($this->initialUrl instanceof BrowserPage && method_exists($this->initialUrl, $method)) {
47+
array_unshift($arguments, $this);
48+
49+
// @phpstan-ignore-next-line method.dynamicName
50+
$this->initialUrl->{$method}(...$arguments);
51+
52+
return $this;
53+
}
54+
55+
throw new BadMethodCallException("Call to undefined method [{$method}].");
56+
}
57+
3558
/**
3659
* Dumps the current page's content and stops the execution.
3760
*/
@@ -68,6 +91,18 @@ public function url(): string
6891
return $this->page->url();
6992
}
7093

94+
/**
95+
* Gets the page's initial URL.
96+
*/
97+
public function initialUrl(): string
98+
{
99+
if ($this->initialUrl instanceof BrowserPage) {
100+
return $this->initialUrl->url();
101+
}
102+
103+
return $this->initialUrl;
104+
}
105+
71106
/**
72107
* Performs a screenshot of the current page and saves it to the given path.
73108
*/

src/Browsable.php

Lines changed: 137 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
use Pest\Browser\Api\ArrayablePendingAwaitablePage;
88
use Pest\Browser\Api\PendingAwaitablePage;
9+
use Pest\Browser\Enums\BrowserType;
910
use Pest\Browser\Enums\Device;
1011
use Pest\Browser\Playwright\Client;
1112
use Pest\Browser\Playwright\Playwright;
13+
use TypeError;
1214

1315
/**
1416
* @internal
@@ -34,14 +36,29 @@ public function __markAsBrowserTest(): void
3436
/**
3537
* Browse to the given URL.
3638
*
37-
* @template TUrl of array<int, string>|string
39+
* @template TUrl of array<int, string|Page>|string|Page
3840
*
3941
* @param TUrl $url
4042
* @param array<string, mixed> $options
41-
* @return (TUrl is array<int, string> ? ArrayablePendingAwaitablePage : PendingAwaitablePage)
43+
* @return (TUrl is array<int, string|Page> ? ArrayablePendingAwaitablePage : PendingAwaitablePage)
4244
*/
43-
public function visit(array|string $url, array $options = []): ArrayablePendingAwaitablePage|PendingAwaitablePage
45+
public function visit(array|string|Page $url, array $options = []): ArrayablePendingAwaitablePage|PendingAwaitablePage
4446
{
47+
if ($url instanceof Page) {
48+
$options = [
49+
...$this->pageTimezone($url),
50+
...$this->pageLocale($url),
51+
...$options,
52+
];
53+
54+
return new PendingAwaitablePage(
55+
$this->pageBrowserType($url),
56+
$this->pageDevice($url),
57+
$url,
58+
$options,
59+
);
60+
}
61+
4562
if (is_string($url)) {
4663
return new PendingAwaitablePage(
4764
Playwright::defaultBrowserType(),
@@ -52,12 +69,123 @@ public function visit(array|string $url, array $options = []): ArrayablePendingA
5269
}
5370

5471
return new ArrayablePendingAwaitablePage(
55-
array_map(fn (string $singleUrl): PendingAwaitablePage => new PendingAwaitablePage(
56-
Playwright::defaultBrowserType(),
57-
Device::DESKTOP,
58-
$singleUrl,
59-
$options,
60-
), $url),
72+
array_map(fn (string|Page $singleUrl): PendingAwaitablePage => $this->visit($singleUrl, $options), $url),
73+
);
74+
}
75+
76+
/**
77+
* Get the locale from page.
78+
*
79+
* @return array{locale?: string}
80+
*
81+
* @throws TypeError
82+
*/
83+
private function pageLocale(Page $page): array
84+
{
85+
if (! method_exists($page, 'locale')) {
86+
return [];
87+
}
88+
89+
$locale = $page->locale();
90+
91+
if (is_string($locale)) {
92+
return ['locale' => $locale];
93+
}
94+
95+
throw new TypeError(
96+
sprintf(
97+
'%s::%s(): Return value must be of type %s, %s returned',
98+
$page::class,
99+
'locale',
100+
'string',
101+
gettype($locale)
102+
)
103+
);
104+
}
105+
106+
/**
107+
* Get the timezone from page.
108+
*
109+
* @return array{timezoneId?: string}
110+
*
111+
* @throws TypeError
112+
*/
113+
private function pageTimezone(Page $page): array
114+
{
115+
if (! method_exists($page, 'timezone')) {
116+
return [];
117+
}
118+
119+
$timezone = $page->timezone();
120+
121+
if (is_string($timezone)) {
122+
return ['timezoneId' => $timezone];
123+
}
124+
125+
throw new TypeError(
126+
sprintf(
127+
'%s::%s(): Return value must be of type %s, %s returned',
128+
$page::class,
129+
'timezone',
130+
'string',
131+
gettype($timezone)
132+
)
133+
);
134+
}
135+
136+
/**
137+
* Get the browser type from page.
138+
*
139+
* @throws TypeError
140+
*/
141+
private function pageBrowserType(Page $page): BrowserType
142+
{
143+
if (! method_exists($page, 'browserType')) {
144+
return Playwright::defaultBrowserType();
145+
}
146+
147+
$browserType = $page->browserType();
148+
149+
if ($browserType instanceof BrowserType) {
150+
return $browserType;
151+
}
152+
153+
throw new TypeError(
154+
sprintf(
155+
'%s::%s(): Return value must be of type %s, %s returned',
156+
$page::class,
157+
'browserType',
158+
BrowserType::class,
159+
gettype($browserType)
160+
)
161+
);
162+
}
163+
164+
/**
165+
* Get the device type from page.
166+
*
167+
* @throws TypeError
168+
*/
169+
private function pageDevice(Page $page): Device
170+
{
171+
if (! method_exists($page, 'device')) {
172+
return Device::DESKTOP;
173+
}
174+
175+
$device = $page->device();
176+
177+
if ($device instanceof Device) {
178+
return $device;
179+
}
180+
181+
throw new TypeError(
182+
sprintf(
183+
'%s::%s(): Return value must be of type %s, %s returned',
184+
$page::class,
185+
'device',
186+
Device::class,
187+
gettype($device)
188+
)
61189
);
62190
}
63191
}

src/Page.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Pest\Browser;
6+
7+
abstract class Page
8+
{
9+
/**
10+
* Get the URL for the page.
11+
*/
12+
abstract public function url(): string;
13+
}

0 commit comments

Comments
 (0)