EventFake.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <?php
  2. namespace Illuminate\Support\Testing\Fakes;
  3. use Closure;
  4. use Illuminate\Contracts\Events\Dispatcher;
  5. use Illuminate\Support\Arr;
  6. use Illuminate\Support\Str;
  7. use Illuminate\Support\Traits\ReflectsClosures;
  8. use PHPUnit\Framework\Assert as PHPUnit;
  9. use ReflectionFunction;
  10. class EventFake implements Dispatcher
  11. {
  12. use ReflectsClosures;
  13. /**
  14. * The original event dispatcher.
  15. *
  16. * @var \Illuminate\Contracts\Events\Dispatcher
  17. */
  18. protected $dispatcher;
  19. /**
  20. * The event types that should be intercepted instead of dispatched.
  21. *
  22. * @var array
  23. */
  24. protected $eventsToFake;
  25. /**
  26. * All of the events that have been intercepted keyed by type.
  27. *
  28. * @var array
  29. */
  30. protected $events = [];
  31. /**
  32. * Create a new event fake instance.
  33. *
  34. * @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
  35. * @param array|string $eventsToFake
  36. * @return void
  37. */
  38. public function __construct(Dispatcher $dispatcher, $eventsToFake = [])
  39. {
  40. $this->dispatcher = $dispatcher;
  41. $this->eventsToFake = Arr::wrap($eventsToFake);
  42. }
  43. /**
  44. * Assert if an event has a listener attached to it.
  45. *
  46. * @param string $expectedEvent
  47. * @param string $expectedListener
  48. * @return void
  49. */
  50. public function assertListening($expectedEvent, $expectedListener)
  51. {
  52. foreach ($this->dispatcher->getListeners($expectedEvent) as $listenerClosure) {
  53. $actualListener = (new ReflectionFunction($listenerClosure))
  54. ->getStaticVariables()['listener'];
  55. if (is_string($actualListener) && Str::endsWith($actualListener, '@handle')) {
  56. $actualListener = Str::parseCallback($actualListener)[0];
  57. }
  58. if ($actualListener === $expectedListener ||
  59. ($actualListener instanceof Closure &&
  60. $expectedListener === Closure::class)) {
  61. PHPUnit::assertTrue(true);
  62. return;
  63. }
  64. }
  65. PHPUnit::assertTrue(
  66. false,
  67. sprintf(
  68. 'Event [%s] does not have the [%s] listener attached to it',
  69. $expectedEvent,
  70. print_r($expectedListener, true)
  71. )
  72. );
  73. }
  74. /**
  75. * Assert if an event was dispatched based on a truth-test callback.
  76. *
  77. * @param string|\Closure $event
  78. * @param callable|int|null $callback
  79. * @return void
  80. */
  81. public function assertDispatched($event, $callback = null)
  82. {
  83. if ($event instanceof Closure) {
  84. [$event, $callback] = [$this->firstClosureParameterType($event), $event];
  85. }
  86. if (is_int($callback)) {
  87. return $this->assertDispatchedTimes($event, $callback);
  88. }
  89. PHPUnit::assertTrue(
  90. $this->dispatched($event, $callback)->count() > 0,
  91. "The expected [{$event}] event was not dispatched."
  92. );
  93. }
  94. /**
  95. * Assert if an event was dispatched a number of times.
  96. *
  97. * @param string $event
  98. * @param int $times
  99. * @return void
  100. */
  101. public function assertDispatchedTimes($event, $times = 1)
  102. {
  103. $count = $this->dispatched($event)->count();
  104. PHPUnit::assertSame(
  105. $times, $count,
  106. "The expected [{$event}] event was dispatched {$count} times instead of {$times} times."
  107. );
  108. }
  109. /**
  110. * Determine if an event was dispatched based on a truth-test callback.
  111. *
  112. * @param string|\Closure $event
  113. * @param callable|null $callback
  114. * @return void
  115. */
  116. public function assertNotDispatched($event, $callback = null)
  117. {
  118. if ($event instanceof Closure) {
  119. [$event, $callback] = [$this->firstClosureParameterType($event), $event];
  120. }
  121. PHPUnit::assertCount(
  122. 0, $this->dispatched($event, $callback),
  123. "The unexpected [{$event}] event was dispatched."
  124. );
  125. }
  126. /**
  127. * Assert that no events were dispatched.
  128. *
  129. * @return void
  130. */
  131. public function assertNothingDispatched()
  132. {
  133. $count = count(Arr::flatten($this->events));
  134. PHPUnit::assertSame(
  135. 0, $count,
  136. "{$count} unexpected events were dispatched."
  137. );
  138. }
  139. /**
  140. * Get all of the events matching a truth-test callback.
  141. *
  142. * @param string $event
  143. * @param callable|null $callback
  144. * @return \Illuminate\Support\Collection
  145. */
  146. public function dispatched($event, $callback = null)
  147. {
  148. if (! $this->hasDispatched($event)) {
  149. return collect();
  150. }
  151. $callback = $callback ?: function () {
  152. return true;
  153. };
  154. return collect($this->events[$event])->filter(function ($arguments) use ($callback) {
  155. return $callback(...$arguments);
  156. });
  157. }
  158. /**
  159. * Determine if the given event has been dispatched.
  160. *
  161. * @param string $event
  162. * @return bool
  163. */
  164. public function hasDispatched($event)
  165. {
  166. return isset($this->events[$event]) && ! empty($this->events[$event]);
  167. }
  168. /**
  169. * Register an event listener with the dispatcher.
  170. *
  171. * @param \Closure|string|array $events
  172. * @param mixed $listener
  173. * @return void
  174. */
  175. public function listen($events, $listener = null)
  176. {
  177. $this->dispatcher->listen($events, $listener);
  178. }
  179. /**
  180. * Determine if a given event has listeners.
  181. *
  182. * @param string $eventName
  183. * @return bool
  184. */
  185. public function hasListeners($eventName)
  186. {
  187. return $this->dispatcher->hasListeners($eventName);
  188. }
  189. /**
  190. * Register an event and payload to be dispatched later.
  191. *
  192. * @param string $event
  193. * @param array $payload
  194. * @return void
  195. */
  196. public function push($event, $payload = [])
  197. {
  198. //
  199. }
  200. /**
  201. * Register an event subscriber with the dispatcher.
  202. *
  203. * @param object|string $subscriber
  204. * @return void
  205. */
  206. public function subscribe($subscriber)
  207. {
  208. $this->dispatcher->subscribe($subscriber);
  209. }
  210. /**
  211. * Flush a set of pushed events.
  212. *
  213. * @param string $event
  214. * @return void
  215. */
  216. public function flush($event)
  217. {
  218. //
  219. }
  220. /**
  221. * Fire an event and call the listeners.
  222. *
  223. * @param string|object $event
  224. * @param mixed $payload
  225. * @param bool $halt
  226. * @return array|null
  227. */
  228. public function dispatch($event, $payload = [], $halt = false)
  229. {
  230. $name = is_object($event) ? get_class($event) : (string) $event;
  231. if ($this->shouldFakeEvent($name, $payload)) {
  232. $this->events[$name][] = func_get_args();
  233. } else {
  234. return $this->dispatcher->dispatch($event, $payload, $halt);
  235. }
  236. }
  237. /**
  238. * Determine if an event should be faked or actually dispatched.
  239. *
  240. * @param string $eventName
  241. * @param mixed $payload
  242. * @return bool
  243. */
  244. protected function shouldFakeEvent($eventName, $payload)
  245. {
  246. if (empty($this->eventsToFake)) {
  247. return true;
  248. }
  249. return collect($this->eventsToFake)
  250. ->filter(function ($event) use ($eventName, $payload) {
  251. return $event instanceof Closure
  252. ? $event($eventName, $payload)
  253. : $event === $eventName;
  254. })
  255. ->isNotEmpty();
  256. }
  257. /**
  258. * Remove a set of listeners from the dispatcher.
  259. *
  260. * @param string $event
  261. * @return void
  262. */
  263. public function forget($event)
  264. {
  265. //
  266. }
  267. /**
  268. * Forget all of the queued listeners.
  269. *
  270. * @return void
  271. */
  272. public function forgetPushed()
  273. {
  274. //
  275. }
  276. /**
  277. * Dispatch an event and call the listeners.
  278. *
  279. * @param string|object $event
  280. * @param mixed $payload
  281. * @return array|null
  282. */
  283. public function until($event, $payload = [])
  284. {
  285. return $this->dispatch($event, $payload, true);
  286. }
  287. }