diff --git a/src/Api/PendingAwaitablePage.php b/src/Api/PendingAwaitablePage.php index 83ad7b77..dbdff301 100644 --- a/src/Api/PendingAwaitablePage.php +++ b/src/Api/PendingAwaitablePage.php @@ -16,6 +16,8 @@ */ final class PendingAwaitablePage { + private const string COOKIES_KEY = '__pestCookies'; + /** * The webpage instance that will be returned when the page is visited. */ @@ -154,6 +156,53 @@ public function geolocation(float $latitude, float $longitude): self ]); } + /** + * Sets a cookie for the browser context. + * + * @param array $options + */ + public function withCookie(string $name, string $value, array $options = []): self + { + return $this->withCookies([['name' => $name, 'value' => $value] + $options]); + } + + /** + * Sets multiple cookies for the browser context. + * + * @param array> $cookies + */ + public function withCookies(array $cookies): self + { + /** @var array> $existing */ + $existing = $this->options[self::COOKIES_KEY] ?? []; + + return new self($this->browserType, $this->device, $this->url, [ + ...$this->options, + self::COOKIES_KEY => [...$existing, ...$cookies], + ]); + } + + /** + * Defaults each cookie's url to the navigation target when neither + * url nor domain is given — Playwright requires one of them. + * + * @param array> $cookies + * @return array> + */ + private static function resolveCookieUrls(array $cookies, string $targetUrl): array + { + return array_map( + static function (array $cookie) use ($targetUrl): array { + if (! isset($cookie['url']) && ! isset($cookie['domain'])) { + $cookie['url'] = $targetUrl; + } + + return $cookie; + }, + $cookies, + ); + } + /** * Creates the webpage instance. */ @@ -170,6 +219,10 @@ private function createAwaitablePage(): AwaitableWebpage */ private function buildAwaitablePage(array $options): AwaitableWebpage { + /** @var array> $cookies */ + $cookies = $options[self::COOKIES_KEY] ?? []; + unset($options[self::COOKIES_KEY]); + $browser = Playwright::browser($this->browserType)->launch(); $context = $browser->newContext([ @@ -184,6 +237,10 @@ private function buildAwaitablePage(array $options): AwaitableWebpage $url = ComputeUrl::from($this->url); + if ($cookies !== []) { + $context->addCookies(self::resolveCookieUrls($cookies, $url)); + } + return new AwaitableWebpage( $context->newPage()->goto($url, $options), $url, diff --git a/src/Playwright/Context.php b/src/Playwright/Context.php index 447d8582..cf178cf7 100644 --- a/src/Playwright/Context.php +++ b/src/Playwright/Context.php @@ -102,4 +102,17 @@ public function addInitScript(string $script): self return $this; } + + /** + * Adds cookies to the browser context. + * + * @param array> $cookies + */ + public function addCookies(array $cookies): self + { + $response = $this->sendMessage('addCookies', ['cookies' => $cookies]); + $this->processVoidResponse($response); + + return $this; + } } diff --git a/tests/Browser/Visit/CookieTest.php b/tests/Browser/Visit/CookieTest.php new file mode 100644 index 00000000..bd5100b8 --- /dev/null +++ b/tests/Browser/Visit/CookieTest.php @@ -0,0 +1,36 @@ + $request->cookies->all()); + + visit('/cookie-check') + ->withCookie('via_browser', 'forwarded') + ->assertSee('"via_browser":"forwarded"'); +}); + +test('may set multiple cookies via withCookies()', function (): void { + Route::get('/cookie-check', fn (Request $request): array => $request->cookies->all()); + + visit('/cookie-check') + ->withCookies([ + ['name' => 'first', 'value' => 'one'], + ['name' => 'second', 'value' => 'two'], + ]) + ->assertSee('"first":"one"') + ->assertSee('"second":"two"'); +}); + +test('subsequent withCookie() calls accumulate', function (): void { + Route::get('/cookie-check', fn (Request $request): array => $request->cookies->all()); + + visit('/cookie-check') + ->withCookie('a', '1') + ->withCookie('b', '2') + ->assertSee('"a":"1"') + ->assertSee('"b":"2"'); +});