Skip to content

Commit 73662ca

Browse files
authored
test: add UI tests (#4)
* test: add UI tests * Fix @nicolas-grekas' review
1 parent 601b394 commit 73662ca

File tree

11 files changed

+372
-190
lines changed

11 files changed

+372
-190
lines changed

CONTRIBUTING.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,19 @@ Start the test app:
88
$ cd tests/app
99
$ yarn encore dev --watch
1010

11-
Convenient endpoints:
11+
## Convenient endpoints:
1212

1313
* `http://localhost:8000`: basic features
1414
* `http://localhost:8000/chat`: chat using Turbo Streams
1515
* `http://localhost:8000/books`: broadcast
16+
17+
## Run tests
18+
19+
### PHP tests
20+
21+
php vendor/bin/simple-phpunit
22+
23+
### JavaScript tests
24+
25+
cd Resources/assets
26+
yarn test

README.md

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Symfony UX Turbo
22

3-
Symfony UX Turbo a Symfony bundle integrating the [Hotwire Turbo](https://turbo.hotwire.dev)
3+
Symfony UX Turbo is a Symfony bundle integrating the [Hotwire Turbo](https://turbo.hotwire.dev)
44
library in Symfony applications. It is part of [the Symfony UX initiative](https://symfony.com/ux).
55

66
Symfony UX Turbo allow having the same user experience as with [Single Page Apps](https://en.wikipedia.org/wiki/Single-page_application)
@@ -105,6 +105,40 @@ class MyController
105105
}
106106
```
107107

108+
#### Writing Tests
109+
110+
Under the hood, Symfony UX Turbo relies on JavaScript to update the HTML page.
111+
To test if your website works properly, you will have to write [UI tests](https://martinfowler.com/articles/practical-test-pyramid.html#UiTests).
112+
113+
Fortunately, we've got you covered! [Symfony Panther](https://github.com/symfony/panther) is a convenient testing tool
114+
using real browsers to test your Symfony application. It shares the same API as BrowserKit, the functional testing tool shipped with Symfony.
115+
116+
[Install Symfony Panther](https://github.com/symfony/panther#installing-panther), and write a test for our Turbo Frame:
117+
118+
```php
119+
// tests/TurboFrameTest.php
120+
namespace App\Tests;
121+
122+
use Symfony\Component\Panther\PantherTestCase;
123+
124+
class TurboFrameTest extends PantherTestCase
125+
{
126+
public function testFrame(): void
127+
{
128+
$client = self::createPantherClient();
129+
$client->request('GET', '/');
130+
131+
$client->clickLink('This block is scoped, the rest of the page will not change if you click here!');
132+
$this->assertSelectorTextContains('body', 'This will replace the content of the Turbo Frame!');
133+
}
134+
}
135+
```
136+
137+
Run `bin/phpunit` to execute the test! Symfony Panther automatically started your application with a web server
138+
and tested it using Google Chrome (Firefox is also supported)!
139+
140+
You can even let Panther open Chrome, and see what happens: `PANTHER_NO_HEADLESS=1 bin/phpunit --debug`
141+
108142
[Read the Turbo Frames documentation](https://turbo.hotwire.dev/handbook/frames) to learn everything you can do using Turbo Frames.
109143

110144
### Coming Alive with Turbo Streams
@@ -442,17 +476,9 @@ However, it is currently considered
442476
[**experimental**](https://symfony.com/doc/current/contributing/code/experimental.html),
443477
meaning it is not bound to Symfony's BC policy for the moment.
444478

445-
## Run tests
479+
## Credits
446480

447-
### PHP tests
481+
Created by [Kévin Dunglas](https://dunglas.fr). Sponsored by [Les-Tilleuls.coop](https://les-tilleuls.coop).
448482

449-
```sh
450-
php vendor/bin/simple-phpunit
451-
```
452-
453-
### JavaScript tests
454-
455-
```sh
456-
cd Resources/assets
457-
yarn test
458-
```
483+
Symfony UX Turbo has been inspired by [hotwired/turbo-rails](https://github.com/hotwired/turbo-rails)
484+
and [sroze/live-twig](https://github.com/sroze/live-twig).

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"symfony/framework-bundle": "^4.4|^5.2",
5050
"symfony/mercure-bundle": "^0.2.6",
5151
"symfony/messenger": "^4.4|^5.2",
52+
"symfony/panther": "^0.9",
5253
"symfony/phpunit-bridge": "^4.4|^5.2",
5354
"symfony/security-core": "5.x-dev",
5455
"symfony/stopwatch": "^4.4|^5.2",

phpunit.xml.dist

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@
88
failOnRisky="true"
99
failOnWarning="true"
1010
>
11+
<php>
12+
<ini name="error_reporting" value="-1" />
13+
<server name="APP_ENV" value="test" force="true" />
14+
<server name="SHELL_VERBOSITY" value="-1" />
15+
<server name="KERNEL_CLASS" value="App\Kernel" />
16+
<server name="PANTHER_WEB_SERVER_DIR" value="./tests/app/public" />
17+
</php>
18+
1119
<testsuites>
1220
<testsuite name="Project Test Suite">
1321
<directory>tests</directory>
@@ -16,11 +24,15 @@
1624

1725
<filter>
1826
<whitelist processUncoveredFilesFromWhitelist="true">
19-
<directory suffix=".php">.</directory>
20-
<exclude>
21-
<directory>tests</directory>
22-
<directory>vendor</directory>
23-
</exclude>
27+
<directory suffix=".php">src</directory>
2428
</whitelist>
2529
</filter>
30+
31+
<listeners>
32+
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
33+
</listeners>
34+
35+
<extensions>
36+
<extension class="Symfony\Component\Panther\ServerExtension" />
37+
</extensions>
2638
</phpunit>

src/Twig/FrameExtension.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ final class FrameExtension extends AbstractExtension
2525
{
2626
public function getFunctions(): iterable
2727
{
28+
yield new TwigFunction('render_turbo_frame', function (string $id, string $src, array $attrs = []): string {
29+
return $this->turboFrameStart($id, $attrs + ['src' => $src]).$this->turboFrameEnd();
30+
}, ['is_safe' => ['html']]);
2831
yield new TwigFunction('turbo_frame_start', [$this, 'turboFrameStart'], ['is_safe' => ['html']]);
2932
yield new TwigFunction('turbo_frame_end', [$this, 'turboFrameEnd'], ['is_safe' => ['html']]);
3033
}

tests/BroadcastTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony UX Turbo package.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Symfony\UX\Turbo\Tests;
15+
16+
use Symfony\Component\Panther\PantherTestCase;
17+
18+
/**
19+
* BroadcastTest.
20+
*
21+
* @author Kévin Dunglas <[email protected]>
22+
*/
23+
class BroadcastTest extends PantherTestCase
24+
{
25+
private const BOOK_TITLE = 'The Ecology of Freedom: The Emergence and Dissolution of Hierarchy';
26+
27+
public function testBroadcast(): void
28+
{
29+
($client = self::createPantherClient())->request('GET', '/books');
30+
$crawler = $client->submitForm('Submit', ['title' => self::BOOK_TITLE]);
31+
32+
$client->waitForElementToContain('#books', self::BOOK_TITLE);
33+
34+
if (!preg_match('/\(#([0-9]+)\)/', $crawler->filter('#books div')->text(), $matches)) {
35+
$this->fail('ID not found');
36+
}
37+
38+
$client->submitForm('Submit', ['id' => $matches[1], 'title' => 'updated']);
39+
$client->waitForElementToContain('#books', 'updated');
40+
41+
$client->submitForm('Submit', ['id' => $matches[1], 'remove' => 'remove']);
42+
$client->waitForElementToNotContain('#books', $matches[1]);
43+
44+
$this->assertTrue(true);
45+
}
46+
}

tests/TurboFrameTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony UX Turbo package.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Symfony\UX\Turbo\Tests;
15+
16+
use Symfony\Component\Panther\PantherTestCase;
17+
18+
/**
19+
* @author Kévin Dunglas <[email protected]>
20+
*/
21+
class TurboFrameTest extends PantherTestCase
22+
{
23+
public function testFrame(): void
24+
{
25+
($client = self::createPantherClient())->request('GET', '/');
26+
27+
$client->clickLink('This block is scoped, the rest of the page will not change if you click here!');
28+
$client->waitForElementToContain('body', 'This will replace the content of the Turbo Frame!');
29+
30+
$this->assertTrue(true);
31+
}
32+
}

tests/TurboStreamTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony UX Turbo package.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Symfony\UX\Turbo\Tests;
15+
16+
use Symfony\Component\Panther\PantherTestCase;
17+
18+
/**
19+
* @author Kévin Dunglas <[email protected]>
20+
*/
21+
final class TurboStreamTest extends PantherTestCase
22+
{
23+
public function testStream(): void
24+
{
25+
($client = self::createPantherClient())->request('GET', '/');
26+
27+
$client->submitForm('Trigger Turbo Stream!');
28+
$client->waitForElementToContain('body', 'This div replace the existing element with the DOM ID "form".');
29+
$this->assertSelectorTextContains('body', 'This div replace the existing element with the DOM ID "form".');
30+
31+
$this->assertTrue(true);
32+
}
33+
}

0 commit comments

Comments
 (0)