NotificationFake.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <?php
  2. namespace Illuminate\Support\Testing\Fakes;
  3. use Closure;
  4. use Exception;
  5. use Illuminate\Contracts\Notifications\Dispatcher as NotificationDispatcher;
  6. use Illuminate\Contracts\Notifications\Factory as NotificationFactory;
  7. use Illuminate\Contracts\Translation\HasLocalePreference;
  8. use Illuminate\Notifications\AnonymousNotifiable;
  9. use Illuminate\Support\Collection;
  10. use Illuminate\Support\Str;
  11. use Illuminate\Support\Traits\Macroable;
  12. use Illuminate\Support\Traits\ReflectsClosures;
  13. use PHPUnit\Framework\Assert as PHPUnit;
  14. class NotificationFake implements NotificationDispatcher, NotificationFactory
  15. {
  16. use Macroable, ReflectsClosures;
  17. /**
  18. * All of the notifications that have been sent.
  19. *
  20. * @var array
  21. */
  22. protected $notifications = [];
  23. /**
  24. * Locale used when sending notifications.
  25. *
  26. * @var string|null
  27. */
  28. public $locale;
  29. /**
  30. * Assert if a notification was sent on-demand based on a truth-test callback.
  31. *
  32. * @param string|\Closure $notification
  33. * @param callable|null $callback
  34. * @return void
  35. *
  36. * @throws \Exception
  37. */
  38. public function assertSentOnDemand($notification, $callback = null)
  39. {
  40. $this->assertSentTo(new AnonymousNotifiable, $notification, $callback);
  41. }
  42. /**
  43. * Assert if a notification was sent based on a truth-test callback.
  44. *
  45. * @param mixed $notifiable
  46. * @param string|\Closure $notification
  47. * @param callable|null $callback
  48. * @return void
  49. *
  50. * @throws \Exception
  51. */
  52. public function assertSentTo($notifiable, $notification, $callback = null)
  53. {
  54. if (is_array($notifiable) || $notifiable instanceof Collection) {
  55. if (count($notifiable) === 0) {
  56. throw new Exception('No notifiable given.');
  57. }
  58. foreach ($notifiable as $singleNotifiable) {
  59. $this->assertSentTo($singleNotifiable, $notification, $callback);
  60. }
  61. return;
  62. }
  63. if ($notification instanceof Closure) {
  64. [$notification, $callback] = [$this->firstClosureParameterType($notification), $notification];
  65. }
  66. if (is_numeric($callback)) {
  67. return $this->assertSentToTimes($notifiable, $notification, $callback);
  68. }
  69. PHPUnit::assertTrue(
  70. $this->sent($notifiable, $notification, $callback)->count() > 0,
  71. "The expected [{$notification}] notification was not sent."
  72. );
  73. }
  74. /**
  75. * Assert if a notification was sent on-demand a number of times.
  76. *
  77. * @param string $notification
  78. * @param int $times
  79. * @return void
  80. */
  81. public function assertSentOnDemandTimes($notification, $times = 1)
  82. {
  83. return $this->assertSentToTimes(new AnonymousNotifiable, $notification, $times);
  84. }
  85. /**
  86. * Assert if a notification was sent a number of times.
  87. *
  88. * @param mixed $notifiable
  89. * @param string $notification
  90. * @param int $times
  91. * @return void
  92. */
  93. public function assertSentToTimes($notifiable, $notification, $times = 1)
  94. {
  95. $count = $this->sent($notifiable, $notification)->count();
  96. PHPUnit::assertSame(
  97. $times, $count,
  98. "Expected [{$notification}] to be sent {$times} times, but was sent {$count} times."
  99. );
  100. }
  101. /**
  102. * Determine if a notification was sent based on a truth-test callback.
  103. *
  104. * @param mixed $notifiable
  105. * @param string|\Closure $notification
  106. * @param callable|null $callback
  107. * @return void
  108. *
  109. * @throws \Exception
  110. */
  111. public function assertNotSentTo($notifiable, $notification, $callback = null)
  112. {
  113. if (is_array($notifiable) || $notifiable instanceof Collection) {
  114. if (count($notifiable) === 0) {
  115. throw new Exception('No notifiable given.');
  116. }
  117. foreach ($notifiable as $singleNotifiable) {
  118. $this->assertNotSentTo($singleNotifiable, $notification, $callback);
  119. }
  120. return;
  121. }
  122. if ($notification instanceof Closure) {
  123. [$notification, $callback] = [$this->firstClosureParameterType($notification), $notification];
  124. }
  125. PHPUnit::assertCount(
  126. 0, $this->sent($notifiable, $notification, $callback),
  127. "The unexpected [{$notification}] notification was sent."
  128. );
  129. }
  130. /**
  131. * Assert that no notifications were sent.
  132. *
  133. * @return void
  134. */
  135. public function assertNothingSent()
  136. {
  137. PHPUnit::assertEmpty($this->notifications, 'Notifications were sent unexpectedly.');
  138. }
  139. /**
  140. * Assert the total amount of times a notification was sent.
  141. *
  142. * @param string $notification
  143. * @param int $expectedCount
  144. * @return void
  145. */
  146. public function assertSentTimes($notification, $expectedCount)
  147. {
  148. $actualCount = collect($this->notifications)
  149. ->flatten(1)
  150. ->reduce(function ($count, $sent) use ($notification) {
  151. return $count + count($sent[$notification] ?? []);
  152. }, 0);
  153. PHPUnit::assertSame(
  154. $expectedCount, $actualCount,
  155. "Expected [{$notification}] to be sent {$expectedCount} times, but was sent {$actualCount} times."
  156. );
  157. }
  158. /**
  159. * Assert the total amount of times a notification was sent.
  160. *
  161. * @param int $expectedCount
  162. * @param string $notification
  163. * @return void
  164. *
  165. * @deprecated Use the assertSentTimes method instead
  166. */
  167. public function assertTimesSent($expectedCount, $notification)
  168. {
  169. $this->assertSentTimes($notification, $expectedCount);
  170. }
  171. /**
  172. * Get all of the notifications matching a truth-test callback.
  173. *
  174. * @param mixed $notifiable
  175. * @param string $notification
  176. * @param callable|null $callback
  177. * @return \Illuminate\Support\Collection
  178. */
  179. public function sent($notifiable, $notification, $callback = null)
  180. {
  181. if (! $this->hasSent($notifiable, $notification)) {
  182. return collect();
  183. }
  184. $callback = $callback ?: function () {
  185. return true;
  186. };
  187. $notifications = collect($this->notificationsFor($notifiable, $notification));
  188. return $notifications->filter(function ($arguments) use ($callback) {
  189. return $callback(...array_values($arguments));
  190. })->pluck('notification');
  191. }
  192. /**
  193. * Determine if there are more notifications left to inspect.
  194. *
  195. * @param mixed $notifiable
  196. * @param string $notification
  197. * @return bool
  198. */
  199. public function hasSent($notifiable, $notification)
  200. {
  201. return ! empty($this->notificationsFor($notifiable, $notification));
  202. }
  203. /**
  204. * Get all of the notifications for a notifiable entity by type.
  205. *
  206. * @param mixed $notifiable
  207. * @param string $notification
  208. * @return array
  209. */
  210. protected function notificationsFor($notifiable, $notification)
  211. {
  212. return $this->notifications[get_class($notifiable)][$notifiable->getKey()][$notification] ?? [];
  213. }
  214. /**
  215. * Send the given notification to the given notifiable entities.
  216. *
  217. * @param \Illuminate\Support\Collection|array|mixed $notifiables
  218. * @param mixed $notification
  219. * @return void
  220. */
  221. public function send($notifiables, $notification)
  222. {
  223. $this->sendNow($notifiables, $notification);
  224. }
  225. /**
  226. * Send the given notification immediately.
  227. *
  228. * @param \Illuminate\Support\Collection|array|mixed $notifiables
  229. * @param mixed $notification
  230. * @param array|null $channels
  231. * @return void
  232. */
  233. public function sendNow($notifiables, $notification, array $channels = null)
  234. {
  235. if (! $notifiables instanceof Collection && ! is_array($notifiables)) {
  236. $notifiables = [$notifiables];
  237. }
  238. foreach ($notifiables as $notifiable) {
  239. if (! $notification->id) {
  240. $notification->id = Str::uuid()->toString();
  241. }
  242. $notifiableChannels = $channels ?: $notification->via($notifiable);
  243. if (method_exists($notification, 'shouldSend')) {
  244. $notifiableChannels = array_filter(
  245. $notifiableChannels,
  246. function ($channel) use ($notification, $notifiable) {
  247. return $notification->shouldSend($notifiable, $channel) !== false;
  248. }
  249. );
  250. if (empty($notifiableChannels)) {
  251. continue;
  252. }
  253. }
  254. $this->notifications[get_class($notifiable)][$notifiable->getKey()][get_class($notification)][] = [
  255. 'notification' => $notification,
  256. 'channels' => $notifiableChannels,
  257. 'notifiable' => $notifiable,
  258. 'locale' => $notification->locale ?? $this->locale ?? value(function () use ($notifiable) {
  259. if ($notifiable instanceof HasLocalePreference) {
  260. return $notifiable->preferredLocale();
  261. }
  262. }),
  263. ];
  264. }
  265. }
  266. /**
  267. * Get a channel instance by name.
  268. *
  269. * @param string|null $name
  270. * @return mixed
  271. */
  272. public function channel($name = null)
  273. {
  274. //
  275. }
  276. /**
  277. * Set the locale of notifications.
  278. *
  279. * @param string $locale
  280. * @return $this
  281. */
  282. public function locale($locale)
  283. {
  284. $this->locale = $locale;
  285. return $this;
  286. }
  287. }