Skip to content

Commit 3173553

Browse files
committed
Events PHPStan fix
1 parent e59a94e commit 3173553

File tree

1 file changed

+116
-74
lines changed

1 file changed

+116
-74
lines changed

src/Codeception/Module/Symfony/EventsAssertionsTrait.php

Lines changed: 116 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,50 @@
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+
$this->handleEventAssertion(false, $expected, false);
3136
}
3237

3338
/**
34-
* Verifies that one or more event listeners were not called during the test.
39+
* Verifies that one or more **listeners** were **not** called during the test.
3540
*
3641
* ```php
3742
* <?php
3843
* $I->dontSeeEventListenerIsCalled('App\MyEventListener');
3944
* $I->dontSeeEventListenerIsCalled(['App\MyEventListener', 'App\MyOtherEventListener']);
40-
* $I->dontSeeEventListenerIsCalled('App\MyEventListener', 'my.event);
45+
* $I->dontSeeEventListenerIsCalled('App\MyEventListener', 'my.event');
4146
* $I->dontSeeEventListenerIsCalled('App\MyEventListener', ['my.event', 'my.other.event']);
4247
* ```
4348
*
44-
* @param class-string|class-string[] $expected
45-
* @param string|string[] $events
49+
* @param class-string|object|list<class-string|object> $expected Listener(s) (class-string or instance) that must **not** be called.
50+
* @param string|list<string> $events Event name(s) to scope the assertion to; empty array means “any event”.
4651
*/
4752
public function dontSeeEventListenerIsCalled(array|object|string $expected, array|string $events = []): void
4853
{
@@ -59,8 +64,8 @@ public function dontSeeEventListenerIsCalled(array|object|string $expected, arra
5964
* $I->dontSeeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']);
6065
* ```
6166
*
62-
* @param object|string|string[] $expected
63-
* @deprecated Use `dontSeeEventListenerIsCalled` instead.
67+
* @param class-string|object|list<class-string|object> $expected
68+
* @deprecated Use {@see dontSeeEventListenerIsCalled()} instead.
6469
*/
6570
public function dontSeeEventTriggered(array|object|string $expected): void
6671
{
@@ -85,49 +90,42 @@ public function dontSeeEventTriggered(array|object|string $expected): void
8590
* $I->dontSeeOrphanEvent(['App\MyEvent', 'App\MyOtherEvent']);
8691
* ```
8792
*
88-
* @param string|string[] $expected
93+
* @param class-string|list<class-string>|null $expected Event class(es) that must NOT appear as orphan.
8994
*/
9095
public function dontSeeOrphanEvent(array|string|null $expected = null): void
9196
{
92-
$actual = [$this->getOrphanedEvents()];
93-
$this->assertEventTriggered(false, $expected, $actual);
97+
$this->handleEventAssertion(false, $expected, true);
9498
}
9599

96100
/**
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.
101+
* Verifies that at least one of the given events **was** dispatched (regular **or** orphan).
102102
*
103103
* ```php
104-
* <?php
105-
* $I->seeEvent('App\MyEvent');
106-
* $I->seeEvent(['App\MyEvent', 'App\MyOtherEvent']);
107-
* ```
104+
* <?php
105+
* $I->seeEvent('App\MyEvent');
106+
* $I->seeEvent(['App\MyEvent', 'App\MyOtherEvent']);
107+
* ```
108108
*
109-
* @param string|string[] $expected
109+
* @param class-string|list<class-string> $expected Fully-qualified class-name(s) of the expected event(s).
110110
*/
111111
public function seeEvent(array|string $expected): void
112112
{
113-
$actualEvents = [...array_column($this->getCalledListeners(), 'event')];
114-
$actual = [$this->getOrphanedEvents(), $actualEvents];
115-
$this->assertEventTriggered(true, $expected, $actual);
113+
$this->handleEventAssertion(true, $expected, false);
116114
}
117115

118116
/**
119-
* Verifies that one or more event listeners were called during the test.
117+
* Verifies that one or more **listeners** were called during the test.
120118
*
121119
* ```php
122120
* <?php
123121
* $I->seeEventListenerIsCalled('App\MyEventListener');
124122
* $I->seeEventListenerIsCalled(['App\MyEventListener', 'App\MyOtherEventListener']);
125-
* $I->seeEventListenerIsCalled('App\MyEventListener', 'my.event);
123+
* $I->seeEventListenerIsCalled('App\MyEventListener', 'my.event');
126124
* $I->seeEventListenerIsCalled('App\MyEventListener', ['my.event', 'my.other.event']);
127125
* ```
128126
*
129-
* @param class-string|class-string[] $expected
130-
* @param string|string[] $events
127+
* @param class-string|object|list<class-string|object> $expected Listeners (class-strings or object instances).
128+
* @param string|list<string> $events Event name(s) (empty = any).
131129
*/
132130
public function seeEventListenerIsCalled(array|object|string $expected, array|string $events = []): void
133131
{
@@ -144,8 +142,8 @@ public function seeEventListenerIsCalled(array|object|string $expected, array|st
144142
* $I->seeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']);
145143
* ```
146144
*
147-
* @param object|string|string[] $expected
148-
* @deprecated Use `seeEventListenerIsCalled` instead.
145+
* @param class-string|object|list<class-string|object> $expected
146+
* @deprecated Use {@see seeEventListenerIsCalled()} instead.
149147
*/
150148
public function seeEventTriggered(array|object|string $expected): void
151149
{
@@ -157,7 +155,7 @@ public function seeEventTriggered(array|object|string $expected): void
157155
}
158156

159157
/**
160-
* Verifies that one or more orphan events were dispatched during the test.
158+
* Verifies that one or more orphan events **were** dispatched during the test.
161159
*
162160
* An orphan event is an event that was triggered by manually executing the
163161
* [`dispatch()`](https://symfony.com/doc/current/components/event_dispatcher.html#dispatch-the-event) method
@@ -169,84 +167,126 @@ public function seeEventTriggered(array|object|string $expected): void
169167
* $I->seeOrphanEvent(['App\MyEvent', 'App\MyOtherEvent']);
170168
* ```
171169
*
172-
* @param string|string[] $expected
170+
* @param class-string|list<class-string> $expected Event class-name(s) expected to be orphan.
173171
*/
174172
public function seeOrphanEvent(array|string $expected): void
175173
{
176-
$actual = [$this->getOrphanedEvents()];
177-
$this->assertEventTriggered(true, $expected, $actual);
174+
$this->handleEventAssertion(true, $expected, true);
178175
}
179176

180-
protected function getCalledListeners(): array
177+
/** @return list<array{event: string, pretty: string}> */
178+
protected function getDispatchedEvents(): array
181179
{
182-
$eventCollector = $this->grabEventCollector(__FUNCTION__);
180+
$eventCollector = $this->grabEventCollector(__FUNCTION__);
183181
$calledListeners = $eventCollector->getCalledListeners($this->getDefaultDispatcher());
184-
return [...$calledListeners->getValue(true)];
182+
$raw = $calledListeners instanceof Data ? $calledListeners->getValue(true) : $calledListeners;
183+
184+
/** @var list<array{event:string,pretty:string}> $events */
185+
$events = is_array($raw) ? array_values($raw) : [];
186+
return $events;
185187
}
186188

189+
/** @return list<string> */
187190
protected function getOrphanedEvents(): array
188191
{
189192
$eventCollector = $this->grabEventCollector(__FUNCTION__);
190-
$orphanedEvents = $eventCollector->getOrphanedEvents($this->getDefaultDispatcher());
191-
return [...$orphanedEvents->getValue(true)];
193+
$orphaned = $eventCollector->getOrphanedEvents($this->getDefaultDispatcher());
194+
$raw = $orphaned instanceof Data ? $orphaned->getValue(true) : $orphaned;
195+
196+
/** @var list<string> $events */
197+
$events = is_array($raw) ? array_values($raw) : [];
198+
return $events;
192199
}
193200

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

198-
if ($assertTrue) $this->assertNotEmpty($actualEvents, 'No event was triggered');
219+
if ($assertTrue) {
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+
$assertTrue,
234+
$wasTriggered,
235+
sprintf("The '%s' event %s triggered", $eventName, $assertTrue ? '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+
bool $assertTrue,
246+
array|object|string $expectedListeners,
247+
array|object|string $expectedEvents
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 ($assertTrue && $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+
$assertTrue,
273+
$wasCalled,
274+
sprintf(
275+
"The '%s' listener was %scalled%s",
276+
$expectedListener,
277+
$assertTrue ? '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)