Skip to content

Commit 72cb88b

Browse files
authored
Merge pull request #194 from negoziator/feature/add-per-visit-host-available
Make ->withHost available when using visit(..)
2 parents d07e6ba + 1bca25a commit 72cb88b

3 files changed

Lines changed: 199 additions & 3 deletions

File tree

src/Api/PendingAwaitablePage.php

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,17 @@ public function withUserAgent(string $userAgent): self
118118
]);
119119
}
120120

121+
/**
122+
* Sets the host for the server.
123+
*/
124+
public function withHost(string $host): self
125+
{
126+
return new self($this->browserType, $this->device, $this->url, [
127+
'host' => $host,
128+
...$this->options,
129+
]);
130+
}
131+
121132
/**
122133
* Sets the timezone for the page.
123134
*/
@@ -147,6 +158,17 @@ public function geolocation(float $latitude, float $longitude): self
147158
* Creates the webpage instance.
148159
*/
149160
private function createAwaitablePage(): AwaitableWebpage
161+
{
162+
$options = $this->options;
163+
$host = $this->extractHost($options);
164+
165+
return $this->withTemporaryHost($host, fn (): AwaitableWebpage => $this->buildAwaitablePage($options));
166+
}
167+
168+
/**
169+
* @param array<string, mixed> $options
170+
*/
171+
private function buildAwaitablePage(array $options): AwaitableWebpage
150172
{
151173
$browser = Playwright::browser($this->browserType)->launch();
152174

@@ -155,16 +177,51 @@ private function createAwaitablePage(): AwaitableWebpage
155177
'timezoneId' => 'UTC',
156178
'colorScheme' => Playwright::defaultColorScheme()->value,
157179
...$this->device->context(),
158-
...$this->options,
180+
...$options,
159181
]);
160182

161183
$context->addInitScript(InitScript::get());
162184

163185
$url = ComputeUrl::from($this->url);
164186

165187
return new AwaitableWebpage(
166-
$context->newPage()->goto($url, $this->options),
188+
$context->newPage()->goto($url, $options),
167189
$url,
168190
);
169191
}
192+
193+
/**
194+
* @param array<string, mixed> &$options
195+
*/
196+
private function extractHost(array &$options): ?string
197+
{
198+
if (! array_key_exists('host', $options)) {
199+
return null;
200+
}
201+
202+
$host = $options['host'];
203+
204+
unset($options['host']);
205+
206+
return is_string($host) ? $host : null;
207+
}
208+
209+
/**
210+
* @param callable(): AwaitableWebpage $callback
211+
*/
212+
private function withTemporaryHost(?string $host, callable $callback): AwaitableWebpage
213+
{
214+
if ($host === null) {
215+
return $callback();
216+
}
217+
218+
$previousHost = Playwright::host();
219+
Playwright::setHost($host);
220+
221+
try {
222+
return $callback();
223+
} finally {
224+
Playwright::setHost($previousHost);
225+
}
226+
}
170227
}

src/Configuration.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public function userAgent(string $userAgent): self
9898
/**
9999
* Sets the host for the server.
100100
*/
101-
public function withHost(string $host): self
101+
public function withHost(?string $host): self
102102
{
103103
Playwright::setHost($host);
104104

tests/Browser/Visit/SubdomainTest.php

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
declare(strict_types=1);
44

55
use Illuminate\Support\Facades\Route;
6+
use Pest\Browser\Playwright\Playwright;
67

78
it('can visit non-subdomain routes with subdomain host browser testing', function (): void {
89
Route::get('/app-test', fn (): string => '
@@ -40,3 +41,141 @@
4041
->assertSee('"subdomain":"api"')
4142
->assertSee('"host":"api.localhost"');
4243
});
44+
45+
it('Can chain withHost on visit', function (): void {
46+
Route::domain('{subdomain}.localhost')->group(function (): void {
47+
Route::get('/api/health', fn (): array => [
48+
'status' => 'ok',
49+
'subdomain' => request()->route('subdomain'),
50+
'host' => request()->getHost(),
51+
]);
52+
});
53+
54+
visit('/api/health')
55+
->withHost('api.localhost')
56+
->assertSee('"status":"ok"')
57+
->assertSee('"subdomain":"api"')
58+
->assertSee('"host":"api.localhost"');
59+
});
60+
61+
it('Chaining withHost will not override global host', function (): void {
62+
Route::domain('{subdomain}.localhost')->group(function (): void {
63+
Route::get('/api/health', fn (): array => [
64+
'subdomain' => request()->route('subdomain'),
65+
'host' => request()->getHost(),
66+
]);
67+
});
68+
69+
Route::get('/', fn (): array => [
70+
'host' => request()->getHost(),
71+
]);
72+
73+
// Set global host: test.domain
74+
pest()->browser()->withHost('test.domain');
75+
76+
// 1. Visit withHost: api.localhost
77+
visit('/api/health')
78+
->withHost('api.localhost')
79+
->assertSee('"host":"api.localhost"')
80+
->assertDontSee('test.domain');
81+
82+
// 2. Visit without withHost: should use global host "test.domain"
83+
visit('/')
84+
->assertSee('"host":"test.domain"')
85+
->assertDontSee('api.localhost');
86+
});
87+
88+
it('uses the first withHost when chained multiple times', function (): void {
89+
// Because of the spread operator "...$this->options" in PendingAwaitablePage
90+
Route::domain('{subdomain}.localhost')->group(function (): void {
91+
Route::get('/api/info', fn (): array => [
92+
'subdomain' => request()->route('subdomain'),
93+
'host' => request()->getHost(),
94+
]);
95+
});
96+
97+
visit('/api/info')
98+
->withHost('first.localhost')
99+
->withHost('api.localhost')
100+
->assertSee('"host":"first.localhost"')
101+
->assertSee('"subdomain":"first"')
102+
->assertDontSee('api.localhost');
103+
});
104+
105+
it('withHost works correctly when combined with other options', function (): void {
106+
Route::domain('{subdomain}.localhost')->group(function (): void {
107+
Route::get('/api/locale', fn (): array => [
108+
'host' => request()->getHost(),
109+
'subdomain' => request()->route('subdomain'),
110+
]);
111+
});
112+
113+
visit('/api/locale')
114+
->withHost('api.localhost')
115+
->inDarkMode()
116+
->assertSee('"host":"api.localhost"')
117+
->assertSee('"subdomain":"api"');
118+
});
119+
120+
it('correctly alternates hosts across multiple visits', function (): void {
121+
Route::domain('{subdomain}.localhost')->group(function (): void {
122+
Route::get('/check', fn (): array => [
123+
'host' => request()->getHost(),
124+
'subdomain' => request()->route('subdomain'),
125+
]);
126+
});
127+
128+
// Visit 1: api.localhost
129+
visit('/check')
130+
->withHost('api.localhost')
131+
->assertSee('"host":"api.localhost"')
132+
->assertSee('"subdomain":"api"');
133+
134+
// Visit 2: admin.localhost
135+
visit('/check')
136+
->withHost('admin.localhost')
137+
->assertSee('"host":"admin.localhost"')
138+
->assertSee('"subdomain":"admin"');
139+
140+
// Visit 3: back to api.localhost
141+
visit('/check')
142+
->withHost('api.localhost')
143+
->assertSee('"host":"api.localhost"')
144+
->assertSee('"subdomain":"api"');
145+
});
146+
147+
it('withHost works when no global host is configured', function (): void {
148+
Route::domain('{subdomain}.localhost')->group(function (): void {
149+
Route::get('/standalone', fn (): array => [
150+
'host' => request()->getHost(),
151+
'subdomain' => request()->route('subdomain'),
152+
]);
153+
});
154+
155+
// No pest()->browser()->withHost() call - just use per-visit withHost
156+
visit('/standalone')
157+
->withHost('custom.localhost')
158+
->assertSee('"host":"custom.localhost"')
159+
->assertSee('"subdomain":"custom"');
160+
});
161+
162+
it('restores global host even when page creation encounters issues', function (): void {
163+
Route::get('/restore-check', fn (): array => [
164+
'host' => request()->getHost(),
165+
]);
166+
167+
$originalHost = 'original.localhost';
168+
pest()->browser()->withHost($originalHost);
169+
170+
// Perform a visit with a different host
171+
visit('/restore-check')
172+
->withHost('temporary.localhost')
173+
->assertSee('"host":"temporary.localhost"');
174+
175+
// Verify global host is still the original after the visit
176+
expect(Playwright::host())->toBe($originalHost);
177+
178+
// Verify next visit without withHost uses the global host
179+
visit('/restore-check')
180+
->assertSee('"host":"original.localhost"');
181+
});

0 commit comments

Comments
 (0)