Skip to content

Commit a9686b0

Browse files
committed
Events PHPStan fix
1 parent e59a94e commit a9686b0

File tree

1 file changed

+122
-80
lines changed

1 file changed

+122
-80
lines changed

src/Codeception/Module/Symfony/EventsAssertionsTrait.php

Lines changed: 122 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,55 @@
44

55
namespace Codeception\Module\Symfony;
66

7+
use PHPUnit\Framework\Assert;
78
use Symfony\Component\HttpKernel\DataCollector\EventDataCollector;
9+
use Symfony\Component\VarDumper\Cloner\Data;
10+
11+
use function array_column;
12+
use function array_merge;
13+
use function in_array;
814
use function is_array;
915
use function is_object;
16+
use function is_string;
17+
use function str_starts_with;
1018

1119
trait EventsAssertionsTrait
1220
{
1321
/**
14-
* Verifies that there were no events during the test.
15-
* Both regular and orphan events are checked.
22+
* Verifies that **no** events (regular **or** orphan) were dispatched during the test.
1623
*
1724
* ```php
18-
* <?php
19-
* $I->dontSeeEvent();
20-
* $I->dontSeeEvent('App\MyEvent');
21-
* $I->dontSeeEvent(['App\MyEvent', 'App\MyOtherEvent']);
22-
* ```
25+
* <?php
26+
* $I->dontSeeEvent();
27+
* $I->dontSeeEvent('App\MyEvent');
28+
* $I->dontSeeEvent(['App\MyEvent', 'App\MyOtherEvent']);
29+
* ```
2330
*
24-
* @param string|string[]|null $expected
31+
* @param class-string|list<class-string>|null $expected Fully-qualified event class(es) that must **not** appear.
2532
*/
2633
public function dontSeeEvent(array|string|null $expected = null): void
2734
{
28-
$actualEvents = [...array_column($this->getCalledListeners(), 'event')];
29-
$actual = [$this->getOrphanedEvents(), $actualEvents];
30-
$this->assertEventTriggered(false, $expected, $actual);
35+
$actual = $this->collectEvents(orphanOnly: false);
36+
$this->assertEventTriggered($expected, $actual, shouldExist: false);
3137
}
3238

3339
/**
34-
* Verifies that one or more event listeners were not called during the test.
40+
* Verifies that one or more **listeners** were **not** called during the test.
3541
*
3642
* ```php
3743
* <?php
3844
* $I->dontSeeEventListenerIsCalled('App\MyEventListener');
3945
* $I->dontSeeEventListenerIsCalled(['App\MyEventListener', 'App\MyOtherEventListener']);
40-
* $I->dontSeeEventListenerIsCalled('App\MyEventListener', 'my.event);
46+
* $I->dontSeeEventListenerIsCalled('App\MyEventListener', 'my.event');
4147
* $I->dontSeeEventListenerIsCalled('App\MyEventListener', ['my.event', 'my.other.event']);
4248
* ```
4349
*
44-
* @param class-string|class-string[] $expected
45-
* @param string|string[] $events
50+
* @param class-string|object|list<class-string|object> $expected Listeners (class-strings or object instances).
51+
* @param string|list<string> $events Event name(s) (empty = any).
4652
*/
4753
public function dontSeeEventListenerIsCalled(array|object|string $expected, array|string $events = []): void
4854
{
49-
$this->assertListenerCalled(false, $expected, $events);
55+
$this->assertListenerCalled($expected, $events, shouldBeCalled: false);
5056
}
5157

5258
/**
@@ -59,8 +65,8 @@ public function dontSeeEventListenerIsCalled(array|object|string $expected, arra
5965
* $I->dontSeeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']);
6066
* ```
6167
*
62-
* @param object|string|string[] $expected
63-
* @deprecated Use `dontSeeEventListenerIsCalled` instead.
68+
* @param class-string|object|list<class-string|object> $expected
69+
* @deprecated Use {@see dontSeeEventListenerIsCalled()} instead.
6470
*/
6571
public function dontSeeEventTriggered(array|object|string $expected): void
6672
{
@@ -75,8 +81,8 @@ public function dontSeeEventTriggered(array|object|string $expected): void
7581
* Verifies that there were no orphan events during the test.
7682
*
7783
* An orphan event is an event that was triggered by manually executing the
78-
* [`dispatch()`](https://symfony.com/doc/current/components/event_dispatcher.html#dispatch-the-event) method
79-
* of the EventDispatcher but was not handled by any listener after it was dispatched.
84+
* {@link https://symfony.com/doc/current/components/event_dispatcher.html#dispatch-the-event dispatch()}
85+
* method of the EventDispatcher but was not handled by any listener after it was dispatched.
8086
*
8187
* ```php
8288
* <?php
@@ -85,53 +91,48 @@ public function dontSeeEventTriggered(array|object|string $expected): void
8591
* $I->dontSeeOrphanEvent(['App\MyEvent', 'App\MyOtherEvent']);
8692
* ```
8793
*
88-
* @param string|string[] $expected
94+
* @param class-string|list<class-string>|null $expected Event class(es) that must **not** appear as orphan.
8995
*/
9096
public function dontSeeOrphanEvent(array|string|null $expected = null): void
9197
{
92-
$actual = [$this->getOrphanedEvents()];
93-
$this->assertEventTriggered(false, $expected, $actual);
98+
$actual = $this->collectEvents(orphanOnly: true);
99+
$this->assertEventTriggered($expected, $actual, shouldExist: false);
94100
}
95101

96102
/**
97-
* Verifies that one or more events were dispatched during the test.
98-
* Both regular and orphan events are checked.
99-
*
100-
* If you need to verify that expected event is not orphan,
101-
* add `dontSeeOrphanEvent` call.
103+
* Verifies that at least one of the given events **was** dispatched (regular **or** orphan).
102104
*
103105
* ```php
104-
* <?php
105-
* $I->seeEvent('App\MyEvent');
106-
* $I->seeEvent(['App\MyEvent', 'App\MyOtherEvent']);
107-
* ```
106+
* <?php
107+
* $I->seeEvent('App\MyEvent');
108+
* $I->seeEvent(['App\MyEvent', 'App\MyOtherEvent']);
109+
* ```
108110
*
109-
* @param string|string[] $expected
111+
* @param class-string|list<class-string> $expected Fully-qualified class-name(s) of the expected event(s).
110112
*/
111113
public function seeEvent(array|string $expected): void
112114
{
113-
$actualEvents = [...array_column($this->getCalledListeners(), 'event')];
114-
$actual = [$this->getOrphanedEvents(), $actualEvents];
115-
$this->assertEventTriggered(true, $expected, $actual);
115+
$actual = $this->collectEvents(orphanOnly: false);
116+
$this->assertEventTriggered($expected, $actual, shouldExist: true);
116117
}
117118

118119
/**
119-
* Verifies that one or more event listeners were called during the test.
120+
* Verifies that one or more **listeners** were called during the test.
120121
*
121122
* ```php
122123
* <?php
123124
* $I->seeEventListenerIsCalled('App\MyEventListener');
124125
* $I->seeEventListenerIsCalled(['App\MyEventListener', 'App\MyOtherEventListener']);
125-
* $I->seeEventListenerIsCalled('App\MyEventListener', 'my.event);
126+
* $I->seeEventListenerIsCalled('App\MyEventListener', 'my.event');
126127
* $I->seeEventListenerIsCalled('App\MyEventListener', ['my.event', 'my.other.event']);
127128
* ```
128129
*
129-
* @param class-string|class-string[] $expected
130-
* @param string|string[] $events
130+
* @param class-string|object|list<class-string|object> $expected Listeners (class-strings or object instances).
131+
* @param string|list<string> $events Event name(s) (empty = any).
131132
*/
132133
public function seeEventListenerIsCalled(array|object|string $expected, array|string $events = []): void
133134
{
134-
$this->assertListenerCalled(true, $expected, $events);
135+
$this->assertListenerCalled($expected, $events, shouldBeCalled: true);
135136
}
136137

137138
/**
@@ -144,8 +145,8 @@ public function seeEventListenerIsCalled(array|object|string $expected, array|st
144145
* $I->seeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']);
145146
* ```
146147
*
147-
* @param object|string|string[] $expected
148-
* @deprecated Use `seeEventListenerIsCalled` instead.
148+
* @param class-string|object|list<class-string|object> $expected
149+
* @deprecated Use {@see seeEventListenerIsCalled()} instead.
149150
*/
150151
public function seeEventTriggered(array|object|string $expected): void
151152
{
@@ -157,96 +158,135 @@ public function seeEventTriggered(array|object|string $expected): void
157158
}
158159

159160
/**
160-
* Verifies that one or more orphan events were dispatched during the test.
161+
* Verifies that one or more orphan events **were** dispatched during the test.
161162
*
162163
* An orphan event is an event that was triggered by manually executing the
163-
* [`dispatch()`](https://symfony.com/doc/current/components/event_dispatcher.html#dispatch-the-event) method
164-
* of the EventDispatcher but was not handled by any listener after it was dispatched.
164+
* {@link https://symfony.com/doc/current/components/event_dispatcher.html#dispatch-the-event dispatch()}
165+
* method of the EventDispatcher but was not handled by any listener after it was dispatched.
165166
*
166167
* ```php
167168
* <?php
168169
* $I->seeOrphanEvent('App\MyEvent');
169170
* $I->seeOrphanEvent(['App\MyEvent', 'App\MyOtherEvent']);
170171
* ```
171172
*
172-
* @param string|string[] $expected
173+
* @param class-string|list<class-string> $expected Event class-name(s) expected to be orphan.
173174
*/
174175
public function seeOrphanEvent(array|string $expected): void
175176
{
176-
$actual = [$this->getOrphanedEvents()];
177-
$this->assertEventTriggered(true, $expected, $actual);
177+
$actual = $this->collectEvents(orphanOnly: true);
178+
$this->assertEventTriggered($expected, $actual, shouldExist: true);
178179
}
179180

180-
protected function getCalledListeners(): array
181+
/** @return list<array{event: string, pretty: string}> */
182+
protected function getDispatchedEvents(): array
181183
{
182-
$eventCollector = $this->grabEventCollector(__FUNCTION__);
184+
$eventCollector = $this->grabEventCollector(__FUNCTION__);
183185
$calledListeners = $eventCollector->getCalledListeners($this->getDefaultDispatcher());
184-
return [...$calledListeners->getValue(true)];
186+
187+
/** @var list<array{event: string, pretty: string}> $events */
188+
$events = is_array($calledListeners) ? array_values($calledListeners) : $calledListeners->getValue(true);
189+
return $events;
185190
}
186191

192+
/** @return list<string> */
187193
protected function getOrphanedEvents(): array
188194
{
189195
$eventCollector = $this->grabEventCollector(__FUNCTION__);
190196
$orphanedEvents = $eventCollector->getOrphanedEvents($this->getDefaultDispatcher());
191-
return [...$orphanedEvents->getValue(true)];
197+
198+
/** @var list<string> $events */
199+
$events = is_array($orphanedEvents) ? array_values($orphanedEvents) : $orphanedEvents->getValue(true);
200+
return $events;
192201
}
193202

194-
protected function assertEventTriggered(bool $assertTrue, array|object|string|null $expected, array $actual): void
203+
/** @return list<list<string>> */
204+
private function collectEvents(bool $orphanOnly): array
205+
{
206+
return $orphanOnly
207+
? [$this->getOrphanedEvents()]
208+
: [$this->getOrphanedEvents(), array_column($this->getDispatchedEvents(), 'event')];
209+
}
210+
211+
/**
212+
* @param class-string|object|list<class-string|object>|null $expected
213+
* @param list<list<string>> $actual
214+
*/
215+
protected function assertEventTriggered(array|object|string|null $expected, array $actual, bool $shouldExist): void
195216
{
196217
$actualEvents = array_merge(...$actual);
197218

198-
if ($assertTrue) $this->assertNotEmpty($actualEvents, 'No event was triggered');
219+
if ($shouldExist) {
220+
$this->assertNotEmpty($actualEvents, 'No event was triggered.');
221+
}
199222
if ($expected === null) {
200223
$this->assertEmpty($actualEvents);
201224
return;
202225
}
203226

204-
$expected = is_object($expected) ? $expected::class : $expected;
205-
foreach ((array)$expected as $expectedEvent) {
206-
$expectedEvent = is_object($expectedEvent) ? $expectedEvent::class : $expectedEvent;
207-
$eventTriggered = in_array($expectedEvent, $actualEvents);
227+
$expectedEvents = is_object($expected) ? [$expected] : (array) $expected;
228+
foreach ($expectedEvents as $expectedEvent) {
229+
$eventName = is_object($expectedEvent) ? $expectedEvent::class : (string) $expectedEvent;
230+
$wasTriggered = in_array($eventName, $actualEvents, true);
208231

209-
$message = $assertTrue
210-
? "The '{$expectedEvent}' event did not trigger"
211-
: "The '{$expectedEvent}' event triggered";
212-
$this->assertSame($assertTrue, $eventTriggered, $message);
232+
$this->assertSame(
233+
$shouldExist,
234+
$wasTriggered,
235+
sprintf("The '%s' event %s triggered", $eventName, $shouldExist ? 'did not' : 'was')
236+
);
213237
}
214238
}
215239

216-
protected function assertListenerCalled(bool $assertTrue, array|object|string $expectedListeners, array|object|string $expectedEvents): void
217-
{
240+
/**
241+
* @param class-string|object|list<class-string|object> $expectedListeners
242+
* @param string|list<string> $expectedEvents
243+
*/
244+
protected function assertListenerCalled(
245+
array|object|string $expectedListeners,
246+
array|object|string $expectedEvents,
247+
bool $shouldBeCalled
248+
): void {
218249
$expectedListeners = is_array($expectedListeners) ? $expectedListeners : [$expectedListeners];
219-
$expectedEvents = is_array($expectedEvents) ? $expectedEvents : [$expectedEvents];
250+
$expectedEvents = is_array($expectedEvents) ? $expectedEvents : [$expectedEvents];
220251

221-
if (empty($expectedEvents)) {
252+
if ($expectedEvents === []) {
222253
$expectedEvents = [null];
223254
} elseif (count($expectedListeners) > 1) {
224-
$this->fail('You cannot check for events when using multiple listeners. Make multiple assertions instead.');
255+
Assert::fail('Cannot check for events when using multiple listeners. Make multiple assertions instead.');
225256
}
226257

227-
$actualEvents = $this->getCalledListeners();
228-
if ($assertTrue && empty($actualEvents)) {
229-
$this->fail('No event listener was called');
258+
$actualEvents = $this->getDispatchedEvents();
259+
260+
if ($shouldBeCalled && $actualEvents === []) {
261+
Assert::fail('No event listener was called.');
230262
}
231263

232264
foreach ($expectedListeners as $expectedListener) {
233-
$expectedListener = is_object($expectedListener) ? $expectedListener::class : $expectedListener;
265+
$expectedListener = is_string($expectedListener) ? $expectedListener : $expectedListener::class;
234266

235267
foreach ($expectedEvents as $expectedEvent) {
236-
$listenerCalled = $this->listenerWasCalled($expectedListener, $expectedEvent, $actualEvents);
237-
$message = "The '{$expectedListener}' listener was called"
238-
. ($expectedEvent ? " for the '{$expectedEvent}' event" : '');
239-
$this->assertSame($assertTrue, $listenerCalled, $message);
268+
$eventName = is_object($expectedEvent) ? $expectedEvent::class : ($expectedEvent ?: null);
269+
$wasCalled = $this->listenerWasCalled($expectedListener, $eventName, $actualEvents);
270+
271+
$this->assertSame(
272+
$shouldBeCalled,
273+
$wasCalled,
274+
sprintf(
275+
"The '%s' listener was %scalled%s",
276+
$expectedListener,
277+
$shouldBeCalled ? 'not ' : '',
278+
$eventName ? " for the '{$eventName}' event" : ''
279+
)
280+
);
240281
}
241282
}
242283
}
243284

285+
/** @param list<array{event: string, pretty: string}> $actualEvents */
244286
private function listenerWasCalled(string $expectedListener, ?string $expectedEvent, array $actualEvents): bool
245287
{
246288
foreach ($actualEvents as $actualEvent) {
247-
if (
248-
isset($actualEvent['pretty'], $actualEvent['event'])
249-
&& str_starts_with($actualEvent['pretty'], $expectedListener)
289+
if (str_starts_with($actualEvent['pretty'], $expectedListener)
250290
&& ($expectedEvent === null || $actualEvent['event'] === $expectedEvent)
251291
) {
252292
return true;
@@ -262,6 +302,8 @@ protected function getDefaultDispatcher(): string
262302

263303
protected function grabEventCollector(string $function): EventDataCollector
264304
{
265-
return $this->grabCollector('events', $function);
305+
/** @var EventDataCollector $collector */
306+
$collector = $this->grabCollector('events', $function);
307+
return $collector;
266308
}
267309
}

0 commit comments

Comments
 (0)