Skip to content

Commit 8e6389c

Browse files
committed
Extract Browser and implement BrowserKit
1 parent 1cd2e28 commit 8e6389c

16 files changed

Lines changed: 1619 additions & 409 deletions

README.md

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,19 @@ return [
190190
playwright:
191191
intercepted_hosts:
192192
- 'localhost'
193-
- '127.0.0.1'
194-
- 'myapp.local'
193+
- '127.0.0.1'
194+
- 'testapp.local'
195195
debug: '%kernel.debug%'
196+
default_browser: 'default'
196197
browsers:
197198
default:
198199
type: 'chromium'
199200
headless: true
200-
default_browser: 'default'
201+
timeout_ms: 30000
202+
slowmo_ms: 0
203+
firefox:
204+
type: 'firefox'
205+
headless: true
201206
```
202207
203208
3. **Create your test base class**:
@@ -259,7 +264,7 @@ class LoginTest extends BaseE2ETest
259264

260265
// Wait for navigation and verify result
261266
$page->waitForLoadState();
262-
$this->assertStringContains('Dashboard', $page->content());
267+
$this->assertPageContains('Dashboard');
263268

264269
// Access intercepted request/response
265270
$response = $this->getLastResponse();
@@ -277,7 +282,7 @@ class LoginTest extends BaseE2ETest
277282
$authCookie = $this->getCookie('AUTH');
278283
$this->assertNotNull($authCookie);
279284

280-
$this->assertStringContains('Admin Panel', $page->content());
285+
$this->assertPageContains('Admin Panel');
281286
}
282287
}
283288
```
@@ -286,7 +291,7 @@ class LoginTest extends BaseE2ETest
286291

287292
**For applications with existing test infrastructure**:
288293

289-
1. Place E2E tests in `tests/E2E/` directory
294+
1. Place E2E tests in `tests/Integration/E2E/` directory (this repository’s convention)
290295
2. Configure separate test database for E2E tests
291296
3. Use environment variables to control test behavior:
292297
- `KERNEL_CLASS` - Custom kernel class
@@ -317,7 +322,7 @@ jobs:
317322
run: npx playwright install chromium --with-deps
318323

319324
- name: Run E2E tests
320-
run: vendor/bin/phpunit tests/E2E/
325+
run: vendor/bin/phpunit tests/Integration/E2E/
321326
env:
322327
PLAYWRIGHT_HEADLESS: true
323328
```
@@ -343,10 +348,9 @@ The bundle provides these methods in `PlaywrightTestCase`:
343348
- `getLastRequest(): ?SymfonyRequest` - Get last intercepted request
344349
- `getLastResponse(): ?SymfonyResponse` - Get last intercepted response
345350

346-
**Hooks (override in your tests):**
351+
**Hooks (public methods you can override):**
347352
- `beforeRequest(SymfonyRequest $request): void` - Called before each request
348353
- `afterResponse(SymfonyResponse $response): void` - Called after each response
349-
- `loadFixtures(array $fixtures): void` - Override to load test data
350354

351355
All browser interactions use the native [Playwright PHP Page API](https://github.com/playwright-php/playwright).
352356

@@ -359,14 +363,14 @@ All browser interactions use the native [Playwright PHP Page API](https://github
359363
## Running Tests
360364

361365
```bash
362-
# Run all E2E tests
363-
vendor/bin/phpunit tests/E2E
366+
# Run all E2E tests (repository convention)
367+
vendor/bin/phpunit tests/Integration/E2E
364368
365369
# Run with visible browser
366-
PLAYWRIGHT_HEADLESS=false vendor/bin/phpunit tests/E2E
370+
PLAYWRIGHT_HEADLESS=false vendor/bin/phpunit tests/Integration/E2E
367371
368-
# Run specific test
369-
vendor/bin/phpunit tests/E2E/UserFlowTest.php
372+
# Run specific test file
373+
vendor/bin/phpunit tests/Integration/E2E/HelloE2ETest.php
370374
```
371375

372376
## Tips and Best Practices
@@ -400,10 +404,10 @@ public function testApiCalls(): void
400404

401405
```bash
402406
# Show browser during tests
403-
PLAYWRIGHT_HEADLESS=false vendor/bin/phpunit tests/E2E/
407+
PLAYWRIGHT_HEADLESS=false vendor/bin/phpunit tests/Integration/E2E/
404408
405409
# Use different browser
406-
PLAYWRIGHT_BROWSER=firefox vendor/bin/phpunit tests/E2E/
410+
PLAYWRIGHT_BROWSER=firefox vendor/bin/phpunit tests/Integration/E2E/
407411
```
408412

409413
## Comparison with WebTestCase
@@ -419,7 +423,7 @@ PLAYWRIGHT_BROWSER=firefox vendor/bin/phpunit tests/E2E/
419423
| Multiple Tabs | ❌ | ✅ |
420424
| Request Interception | ✅ | ✅ |
421425
| Symfony Integration | ✅ | ✅ |
422-
| Transaction Rollback | | ✅ |
426+
| Transaction Rollback | Depends | Depends |
423427

424428
## Contributing
425429

phpunit.xml.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
</testsuite>
1717
</testsuites>
1818

19-
<source ignoreIndirectDeprecations="true" restrictNotices="true" restrictWarnings="true">
19+
<source restrictNotices="true" restrictWarnings="true">
2020
<include>
2121
<directory>src</directory>
2222
</include>

src/Browser/PlaywrightBrowser.php

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the playwright-php/playwright package.
7+
* For the full copyright and license information, please view
8+
* the LICENSE file that was distributed with this source code.
9+
*/
10+
11+
namespace PlaywrightPHP\Symfony\Browser;
12+
13+
use PlaywrightPHP\Browser\BrowserContextInterface;
14+
use PlaywrightPHP\Page\PageInterface;
15+
use PlaywrightPHP\Playwright;
16+
17+
/**
18+
* Handles Playwright browser lifecycle and configuration.
19+
*
20+
* Responsibilities:
21+
* - Browser startup/shutdown
22+
* - Page creation and management
23+
* - Browser configuration (headless, type, etc.)
24+
* - Routing setup for request interception
25+
*
26+
* @author Simon André <smn.andre@gmail.com>
27+
*/
28+
class PlaywrightBrowser
29+
{
30+
private ?BrowserContextInterface $context = null;
31+
private ?PageInterface $page = null;
32+
33+
public function __construct(
34+
private readonly string $browserType = 'chromium',
35+
private readonly bool $headless = true,
36+
private readonly array $launchOptions = [],
37+
) {
38+
}
39+
40+
public function start(): void
41+
{
42+
if (null !== $this->context) {
43+
return;
44+
}
45+
46+
$options = array_merge(
47+
['headless' => $this->headless],
48+
$this->launchOptions
49+
);
50+
51+
$this->context = match ($this->browserType) {
52+
'firefox' => Playwright::firefox($options),
53+
'webkit' => Playwright::webkit($options),
54+
default => Playwright::chromium($options),
55+
};
56+
57+
$this->page = $this->context->newPage();
58+
}
59+
60+
public function stop(): void
61+
{
62+
$this->context?->close();
63+
$this->context = null;
64+
$this->page = null;
65+
}
66+
67+
public function getContext(): ?BrowserContextInterface
68+
{
69+
return $this->context;
70+
}
71+
72+
public function getPage(): ?PageInterface
73+
{
74+
$this->ensureStarted();
75+
76+
return $this->page;
77+
}
78+
79+
public function setupRouting(callable $routeHandler): void
80+
{
81+
$this->ensureStarted();
82+
$this->page->route('**/*', $routeHandler);
83+
}
84+
85+
public function isHeadless(): bool
86+
{
87+
return $this->headless;
88+
}
89+
90+
public function getBrowserType(): string
91+
{
92+
return $this->browserType;
93+
}
94+
95+
public static function fromEnvironment(): self
96+
{
97+
$browserType = strtolower((string) getenv('PLAYWRIGHT_BROWSER'));
98+
if (!in_array($browserType, ['chromium', 'firefox', 'webkit'], true)) {
99+
$browserType = 'chromium';
100+
}
101+
102+
$headless = 'false' !== getenv('PLAYWRIGHT_HEADLESS');
103+
104+
return new self($browserType, $headless);
105+
}
106+
107+
private function ensureStarted(): void
108+
{
109+
if (null === $this->context) {
110+
$this->start();
111+
}
112+
}
113+
}

0 commit comments

Comments
 (0)