Skip to content

Commit 518ccdb

Browse files
authored
Merge pull request #3 from coldfrontlabs/codeception5
Codeception5
2 parents deeba54 + ab81460 commit 518ccdb

File tree

7 files changed

+382
-23
lines changed

7 files changed

+382
-23
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.idea/
22
composer.lock
33
vendor/
4-
core/
4+
core/
5+
modules/

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,17 @@ Includes:
99
- Drupal Drush
1010
- Drupal Acceptance
1111

12+
## Differences from guncha25/drupal-codeception
13+
14+
- PHP 8 support
15+
- No dependency on Faker
16+
- Various fixes
17+
1218
## Installation
1319

1420
Require package:
1521

16-
```composer require guncha25/drupal-codeception --dev```
22+
```composer require coldfrontlabs/drupal-codeception --dev```
1723

1824
If codeception was not previously set up:
1925

composer.json

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "guncha25/drupal-codeception",
2+
"name": "coldfrontlabs/drupal-codeception",
33
"description": "Codeception toolset for Drupal testing.",
44
"type": "package",
55
"repositories": [
@@ -9,17 +9,20 @@
99
}
1010
],
1111
"require": {
12-
"codeception/codeception": "^4.0",
13-
"fzaninotto/faker": "^1.8",
14-
"codeception/module-webdriver": "^1.1",
15-
"webflo/drupal-finder": "^1.2"
12+
"codeception/codeception": "^4.0 || ^5.0",
13+
"codeception/module-webdriver": "*",
14+
"webflo/drupal-finder": "*"
1615
},
1716
"require-dev": {
18-
"composer/installers": "^1",
19-
"drupal/core": "^8"
17+
"composer/installers": "*",
18+
"drupal/core": "^8 || ^9 || ^10"
2019
},
2120
"license": "GPL-2.0",
2221
"authors": [
22+
{
23+
"name": "Mathew Winstone",
24+
"email": "[email protected]"
25+
},
2326
{
2427
"name": "Guntis Jakovins",
2528
"email": "[email protected]"
@@ -30,5 +33,10 @@
3033
"psr-4": {
3134
"Codeception\\": "src/Codeception"
3235
}
36+
},
37+
"config": {
38+
"allow-plugins": {
39+
"composer/installers": true
40+
}
3341
}
3442
}

src/Codeception/Module/DrupalBootstrap.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use Codeception\TestDrupalKernel;
99
use Symfony\Component\HttpFoundation\Request;
1010
use DrupalFinder\DrupalFinder;
11-
11+
use Codeception\Module\DrupalBootstrap\EventsAssertionsTrait;
1212

1313
/**
1414
* Class DrupalBootstrap.
@@ -25,6 +25,8 @@
2525
*/
2626
class DrupalBootstrap extends Module {
2727

28+
use EventsAssertionsTrait;
29+
2830
/**
2931
* Default module configuration.
3032
*
@@ -34,6 +36,13 @@ class DrupalBootstrap extends Module {
3436
'site_path' => 'sites/default',
3537
];
3638

39+
/**
40+
* Track wether we enabled the webprofiler module or not.
41+
*
42+
* @var bool
43+
*/
44+
protected $enabledWebProfiler = FALSE;
45+
3746
/**
3847
* DrupalBootstrap constructor.
3948
*
@@ -72,4 +81,25 @@ public function __construct(ModuleContainer $container, $config = NULL) {
7281
$kernel->bootTestEnvironment($this->_getConfig('site_path'), $request);
7382
}
7483

84+
/**
85+
* Enabled dependent modules.
86+
*/
87+
public function _beforeSuite($settings = []) {
88+
$module_handler = \Drupal::service('module_handler');
89+
if (!$module_handler->moduleExists('webprofiler')) {
90+
$this->enabledWebProfiler = TRUE;
91+
\Drupal::service('module_installer')->install(['webprofiler']);
92+
}
93+
}
94+
95+
/**
96+
* Disable modules which were enabled.
97+
*/
98+
public function _afterSuite($settings = []) {
99+
if ($this->enabledWebProfiler) {
100+
$this->enabledWebProfiler = FALSE;
101+
\Drupal::service('module_installer')->uninstall(['webprofiler']);
102+
}
103+
}
104+
75105
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Codeception\Module\DrupalBootstrap;
6+
7+
use Drupal\webprofiler\DataCollector\EventsDataCollector;
8+
use Drupal\webprofiler\EventDispatcher\EventDispatcherTraceableInterface;
9+
use function get_class;
10+
use function is_array;
11+
use function is_object;
12+
13+
/**
14+
*
15+
*/
16+
trait EventsAssertionsTrait {
17+
18+
/**
19+
* Verifies that there were no orphan events during the test.
20+
*
21+
* An orphan event is an event that was triggered by manually executing the
22+
* [`dispatch()`](https://symfony.com/doc/current/components/event_dispatcher.html#dispatch-the-event) method
23+
* of the EventDispatcher but was not handled by any listener after it was dispatched.
24+
*
25+
* ```php
26+
* <?php
27+
* $I->dontSeeOrphanEvent();
28+
* $I->dontSeeOrphanEvent('App\MyEvent');
29+
* $I->dontSeeOrphanEvent(new App\Events\MyEvent());
30+
* $I->dontSeeOrphanEvent(['App\MyEvent', 'App\MyOtherEvent']);
31+
* ```
32+
*
33+
* @param string|object|string[] $expected
34+
*/
35+
public function dontSeeOrphanEvent($expected = NULL): void {
36+
$eventCollector = $this->grabEventCollector();
37+
38+
$data = $eventCollector->getOrphanedEvents();
39+
$expected = is_array($expected) ? $expected : [$expected];
40+
41+
if ($expected === NULL) {
42+
$this->assertSame(0, count($data));
43+
}
44+
else {
45+
$this->assertEventNotTriggered($data, $expected);
46+
}
47+
}
48+
49+
/**
50+
* Verifies that one or more event listeners were not called during the test.
51+
*
52+
* ```php
53+
* <?php
54+
* $I->dontSeeEventTriggered('App\MyEvent');
55+
* $I->dontSeeEventTriggered(new App\Events\MyEvent());
56+
* $I->dontSeeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']);
57+
* $I->dontSeeEventTriggered('my_event_string_name');
58+
* $I->dontSeeEventTriggered(['my_event_string', 'my_other_event_string]);
59+
* ```
60+
*
61+
* @param string|object|string[] $expected
62+
*/
63+
public function dontSeeEventTriggered($expected): void {
64+
$eventCollector = $this->grabEventCollector();
65+
66+
$data = $eventCollector->getCalledListeners();
67+
$expected = is_array($expected) ? $expected : [$expected];
68+
69+
$this->assertEventNotTriggered($data, $expected);
70+
}
71+
72+
/**
73+
* Verifies that one or more orphan events were dispatched during the test.
74+
*
75+
* An orphan event is an event that was triggered by manually executing the
76+
* [`dispatch()`](https://symfony.com/doc/current/components/event_dispatcher.html#dispatch-the-event) method
77+
* of the EventDispatcher but was not handled by any listener after it was dispatched.
78+
*
79+
* ```php
80+
* <?php
81+
* $I->seeOrphanEvent('App\MyEvent');
82+
* $I->seeOrphanEvent(new App\Events\MyEvent());
83+
* $I->seeOrphanEvent(['App\MyEvent', 'App\MyOtherEvent']);
84+
* $I->seeOrphanEvent('my_event_string_name');
85+
* $I->seeOrphanEvent(['my_event_string_name', 'my_other_event_string]);
86+
* ```
87+
*
88+
* @param string|object|string[] $expected
89+
*/
90+
public function seeOrphanEvent($expected): void {
91+
$eventCollector = $this->grabEventCollector();
92+
93+
$data = $eventCollector->getOrphanedEvents();
94+
$expected = is_array($expected) ? $expected : [$expected];
95+
96+
$this->assertEventTriggered($data, $expected);
97+
}
98+
99+
/**
100+
* Verifies that one or more event listeners were called during the test.
101+
*
102+
* ```php
103+
* <?php
104+
* $I->seeEventTriggered('App\MyEvent');
105+
* $I->seeEventTriggered(new App\Events\MyEvent());
106+
* $I->seeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']);
107+
* $I->seeEventTriggered('my_event_string_name');
108+
* $I->seeEventTriggered(['my_event_string_name', 'my_other_event_string]);
109+
* ```
110+
*
111+
* @param string|object|string[] $expected
112+
*/
113+
public function seeEventTriggered($expected): void {
114+
$eventCollector = $this->grabEventCollector();
115+
116+
$data = $eventCollector->getCalledListeners();
117+
$expected = is_array($expected) ? $expected : [$expected];
118+
119+
$this->assertEventTriggered($data, $expected);
120+
}
121+
122+
/**
123+
*
124+
*/
125+
protected function assertEventNotTriggered(array $data, array $expected): void {
126+
foreach ($expected as $expectedEvent) {
127+
$expectedEvent = is_object($expectedEvent) ? get_class($expectedEvent) : $expectedEvent;
128+
$this->assertFalse(
129+
$this->eventWasTriggered($data, (string) $expectedEvent),
130+
"The '{$expectedEvent}' event triggered"
131+
);
132+
}
133+
}
134+
135+
/**
136+
*
137+
*/
138+
protected function assertEventTriggered(array $data, array $expected): void {
139+
if (count($data) === 0) {
140+
$this->fail('No event was triggered');
141+
}
142+
143+
foreach ($expected as $expectedEvent) {
144+
$expectedEvent = is_object($expectedEvent) ? get_class($expectedEvent) : $expectedEvent;
145+
$this->assertTrue(
146+
$this->eventWasTriggered($data, (string) $expectedEvent),
147+
"The '{$expectedEvent}' event did not trigger"
148+
);
149+
}
150+
}
151+
152+
/**
153+
*
154+
*/
155+
protected function eventWasTriggered(array $actual, string $expectedEvent): bool {
156+
$triggered = FALSE;
157+
158+
foreach ($actual as $name => $actualEvent) {
159+
// Called Listeners.
160+
if ($name === $expectedEvent && !empty($actualEvent)) {
161+
$triggered = TRUE;
162+
}
163+
}
164+
165+
return $triggered;
166+
}
167+
168+
/**
169+
* Get the event data collector service.
170+
*/
171+
protected function grabEventCollector(): EventsDataCollector {
172+
$event_dispatcher = \Drupal::service('event_dispatcher');
173+
if ($event_dispatcher instanceof EventDispatcherTraceableInterface) {
174+
$collector = new EventsDataCollector($event_dispatcher);
175+
$collector->lateCollect();
176+
return $collector;
177+
}
178+
else {
179+
throw new \Exception('Webprofiler module is required for testing events.');
180+
}
181+
}
182+
183+
}

0 commit comments

Comments
 (0)