PromiseRejectedTestTrait.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. <?php
  2. namespace React\Promise\PromiseTest;
  3. use Exception;
  4. use InvalidArgumentException;
  5. use React\Promise\PromiseAdapter\PromiseAdapterInterface;
  6. use React\Promise\PromiseInterface;
  7. use function React\Promise\reject;
  8. use function React\Promise\resolve;
  9. trait PromiseRejectedTestTrait
  10. {
  11. abstract public function getPromiseTestAdapter(callable $canceller = null): PromiseAdapterInterface;
  12. /** @test */
  13. public function rejectedPromiseShouldBeImmutable(): void
  14. {
  15. $adapter = $this->getPromiseTestAdapter();
  16. $exception1 = new Exception();
  17. $exception2 = new Exception();
  18. $mock = $this->createCallableMock();
  19. $mock
  20. ->expects($this->once())
  21. ->method('__invoke')
  22. ->with($this->identicalTo($exception1));
  23. $adapter->reject($exception1);
  24. $adapter->reject($exception2);
  25. $adapter->promise()
  26. ->then(
  27. $this->expectCallableNever(),
  28. $mock
  29. );
  30. }
  31. /** @test */
  32. public function rejectedPromiseShouldInvokeNewlyAddedCallback(): void
  33. {
  34. $adapter = $this->getPromiseTestAdapter();
  35. $exception = new Exception();
  36. $adapter->reject($exception);
  37. $mock = $this->createCallableMock();
  38. $mock
  39. ->expects($this->once())
  40. ->method('__invoke')
  41. ->with($this->identicalTo($exception));
  42. $adapter->promise()
  43. ->then($this->expectCallableNever(), $mock);
  44. }
  45. /** @test */
  46. public function shouldForwardUndefinedRejectionValue(): void
  47. {
  48. $adapter = $this->getPromiseTestAdapter();
  49. $mock = $this->createCallableMock();
  50. $mock
  51. ->expects($this->once())
  52. ->method('__invoke')
  53. ->with(null);
  54. $adapter->reject(new Exception());
  55. $adapter->promise()
  56. ->then(
  57. $this->expectCallableNever(),
  58. function () {
  59. // Presence of rejection handler is enough to switch back
  60. // to resolve mode, even though it returns undefined.
  61. // The ONLY way to propagate a rejection is to re-throw or
  62. // return a rejected promise;
  63. }
  64. )
  65. ->then(
  66. $mock,
  67. $this->expectCallableNever()
  68. );
  69. }
  70. /** @test */
  71. public function shouldSwitchFromErrbacksToCallbacksWhenErrbackDoesNotExplicitlyPropagate(): void
  72. {
  73. $adapter = $this->getPromiseTestAdapter();
  74. $mock = $this->createCallableMock();
  75. $mock
  76. ->expects($this->once())
  77. ->method('__invoke')
  78. ->with($this->identicalTo(2));
  79. $adapter->reject(new Exception());
  80. $adapter->promise()
  81. ->then(
  82. $this->expectCallableNever(),
  83. function () {
  84. return 2;
  85. }
  86. )
  87. ->then(
  88. $mock,
  89. $this->expectCallableNever()
  90. );
  91. }
  92. /** @test */
  93. public function shouldSwitchFromErrbacksToCallbacksWhenErrbackReturnsAResolution(): void
  94. {
  95. $adapter = $this->getPromiseTestAdapter();
  96. $mock = $this->createCallableMock();
  97. $mock
  98. ->expects($this->once())
  99. ->method('__invoke')
  100. ->with($this->identicalTo(2));
  101. $adapter->reject(new Exception());
  102. $adapter->promise()
  103. ->then(
  104. $this->expectCallableNever(),
  105. function () {
  106. return resolve(2);
  107. }
  108. )
  109. ->then(
  110. $mock,
  111. $this->expectCallableNever()
  112. );
  113. }
  114. /** @test */
  115. public function shouldPropagateRejectionsWhenErrbackThrows(): void
  116. {
  117. $adapter = $this->getPromiseTestAdapter();
  118. $exception = new Exception();
  119. $mock = $this->createCallableMock();
  120. $mock
  121. ->expects($this->once())
  122. ->method('__invoke')
  123. ->will($this->throwException($exception));
  124. $mock2 = $this->createCallableMock();
  125. $mock2
  126. ->expects($this->once())
  127. ->method('__invoke')
  128. ->with($this->identicalTo($exception));
  129. $adapter->reject(new Exception());
  130. $adapter->promise()
  131. ->then(
  132. $this->expectCallableNever(),
  133. $mock
  134. )
  135. ->then(
  136. $this->expectCallableNever(),
  137. $mock2
  138. );
  139. }
  140. /** @test */
  141. public function shouldPropagateRejectionsWhenErrbackReturnsARejection(): void
  142. {
  143. $adapter = $this->getPromiseTestAdapter();
  144. $exception = new Exception();
  145. $mock = $this->createCallableMock();
  146. $mock
  147. ->expects($this->once())
  148. ->method('__invoke')
  149. ->with($this->identicalTo($exception));
  150. $adapter->reject(new Exception());
  151. $adapter->promise()
  152. ->then(
  153. $this->expectCallableNever(),
  154. function () use ($exception) {
  155. return reject($exception);
  156. }
  157. )
  158. ->then(
  159. $this->expectCallableNever(),
  160. $mock
  161. );
  162. }
  163. /** @test */
  164. public function catchShouldInvokeRejectionHandlerForRejectedPromise(): void
  165. {
  166. $adapter = $this->getPromiseTestAdapter();
  167. $exception = new Exception();
  168. $mock = $this->createCallableMock();
  169. $mock
  170. ->expects($this->once())
  171. ->method('__invoke')
  172. ->with($this->identicalTo($exception));
  173. $adapter->reject($exception);
  174. $adapter->promise()->catch($mock);
  175. }
  176. /** @test */
  177. public function catchShouldInvokeNonTypeHintedRejectionHandlerIfReasonIsAnExceptionForRejectedPromise(): void
  178. {
  179. $adapter = $this->getPromiseTestAdapter();
  180. $exception = new Exception();
  181. $mock = $this->createCallableMock();
  182. $mock
  183. ->expects($this->once())
  184. ->method('__invoke')
  185. ->with($this->identicalTo($exception));
  186. $adapter->reject($exception);
  187. $adapter->promise()
  188. ->catch(function ($reason) use ($mock) {
  189. $mock($reason);
  190. });
  191. }
  192. /** @test */
  193. public function catchShouldInvokeRejectionHandlerIfReasonMatchesTypehintForRejectedPromise(): void
  194. {
  195. $adapter = $this->getPromiseTestAdapter();
  196. $exception = new InvalidArgumentException();
  197. $mock = $this->createCallableMock();
  198. $mock
  199. ->expects($this->once())
  200. ->method('__invoke')
  201. ->with($this->identicalTo($exception));
  202. $adapter->reject($exception);
  203. $adapter->promise()
  204. ->catch(function (InvalidArgumentException $reason) use ($mock) {
  205. $mock($reason);
  206. });
  207. }
  208. /** @test */
  209. public function catchShouldNotInvokeRejectionHandlerIfReaonsDoesNotMatchTypehintForRejectedPromise(): void
  210. {
  211. $adapter = $this->getPromiseTestAdapter();
  212. $exception = new Exception();
  213. $mock = $this->expectCallableNever();
  214. $adapter->reject($exception);
  215. $adapter->promise()
  216. ->catch(function (InvalidArgumentException $reason) use ($mock) {
  217. $mock($reason);
  218. })->then(null, $this->expectCallableOnce()); // avoid reporting unhandled rejection
  219. }
  220. /** @test */
  221. public function finallyShouldNotSuppressRejectionForRejectedPromise(): void
  222. {
  223. $adapter = $this->getPromiseTestAdapter();
  224. $exception = new Exception();
  225. $mock = $this->createCallableMock();
  226. $mock
  227. ->expects($this->once())
  228. ->method('__invoke')
  229. ->with($this->identicalTo($exception));
  230. $adapter->reject($exception);
  231. $adapter->promise()
  232. ->finally(function () {})
  233. ->then(null, $mock);
  234. }
  235. /** @test */
  236. public function finallyShouldNotSuppressRejectionWhenHandlerReturnsANonPromiseForRejectedPromise(): void
  237. {
  238. $adapter = $this->getPromiseTestAdapter();
  239. $exception = new Exception();
  240. $mock = $this->createCallableMock();
  241. $mock
  242. ->expects($this->once())
  243. ->method('__invoke')
  244. ->with($this->identicalTo($exception));
  245. $adapter->reject($exception);
  246. $adapter->promise()
  247. ->finally(function (): int { // @phpstan-ignore-line
  248. return 1;
  249. })
  250. ->then(null, $mock);
  251. }
  252. /** @test */
  253. public function finallyShouldNotSuppressRejectionWhenHandlerReturnsAPromiseForRejectedPromise(): void
  254. {
  255. $adapter = $this->getPromiseTestAdapter();
  256. $exception = new Exception();
  257. $mock = $this->createCallableMock();
  258. $mock
  259. ->expects($this->once())
  260. ->method('__invoke')
  261. ->with($this->identicalTo($exception));
  262. $adapter->reject($exception);
  263. $adapter->promise()
  264. ->finally(function (): PromiseInterface { // @phpstan-ignore-line
  265. return resolve(1);
  266. })
  267. ->then(null, $mock);
  268. }
  269. /** @test */
  270. public function finallyShouldRejectWhenHandlerThrowsForRejectedPromise(): void
  271. {
  272. $adapter = $this->getPromiseTestAdapter();
  273. $exception1 = new Exception();
  274. $exception2 = new Exception();
  275. $mock = $this->createCallableMock();
  276. $mock
  277. ->expects($this->once())
  278. ->method('__invoke')
  279. ->with($this->identicalTo($exception2));
  280. $adapter->reject($exception1);
  281. $adapter->promise()
  282. ->finally(function () use ($exception2) {
  283. throw $exception2;
  284. })
  285. ->then(null, $mock);
  286. }
  287. /** @test */
  288. public function finallyShouldRejectWhenHandlerRejectsForRejectedPromise(): void
  289. {
  290. $adapter = $this->getPromiseTestAdapter();
  291. $exception1 = new Exception();
  292. $exception2 = new Exception();
  293. $mock = $this->createCallableMock();
  294. $mock
  295. ->expects($this->once())
  296. ->method('__invoke')
  297. ->with($this->identicalTo($exception2));
  298. $adapter->reject($exception1);
  299. $adapter->promise()
  300. ->finally(function () use ($exception2) {
  301. return reject($exception2);
  302. })
  303. ->then(null, $mock);
  304. }
  305. /** @test */
  306. public function cancelShouldHaveNoEffectForRejectedPromise(): void
  307. {
  308. $adapter = $this->getPromiseTestAdapter($this->expectCallableNever());
  309. $adapter->reject(new Exception());
  310. $adapter->promise()->cancel();
  311. }
  312. /**
  313. * @test
  314. * @deprecated
  315. */
  316. public function otherwiseShouldInvokeRejectionHandlerForRejectedPromise(): void
  317. {
  318. $adapter = $this->getPromiseTestAdapter();
  319. $exception = new Exception();
  320. $mock = $this->createCallableMock();
  321. $mock
  322. ->expects($this->once())
  323. ->method('__invoke')
  324. ->with($this->identicalTo($exception));
  325. $adapter->reject($exception);
  326. $adapter->promise()->otherwise($mock);
  327. }
  328. /**
  329. * @test
  330. * @deprecated
  331. */
  332. public function otherwiseShouldInvokeNonTypeHintedRejectionHandlerIfReasonIsAnExceptionForRejectedPromise(): void
  333. {
  334. $adapter = $this->getPromiseTestAdapter();
  335. $exception = new Exception();
  336. $mock = $this->createCallableMock();
  337. $mock
  338. ->expects($this->once())
  339. ->method('__invoke')
  340. ->with($this->identicalTo($exception));
  341. $adapter->reject($exception);
  342. $adapter->promise()
  343. ->otherwise(function ($reason) use ($mock) {
  344. $mock($reason);
  345. });
  346. }
  347. /**
  348. * @test
  349. * @deprecated
  350. */
  351. public function otherwiseShouldInvokeRejectionHandlerIfReasonMatchesTypehintForRejectedPromise(): void
  352. {
  353. $adapter = $this->getPromiseTestAdapter();
  354. $exception = new InvalidArgumentException();
  355. $mock = $this->createCallableMock();
  356. $mock
  357. ->expects($this->once())
  358. ->method('__invoke')
  359. ->with($this->identicalTo($exception));
  360. $adapter->reject($exception);
  361. $adapter->promise()
  362. ->otherwise(function (InvalidArgumentException $reason) use ($mock) {
  363. $mock($reason);
  364. });
  365. }
  366. /**
  367. * @test
  368. * @deprecated
  369. */
  370. public function otherwiseShouldNotInvokeRejectionHandlerIfReaonsDoesNotMatchTypehintForRejectedPromise(): void
  371. {
  372. $adapter = $this->getPromiseTestAdapter();
  373. $exception = new Exception();
  374. $mock = $this->expectCallableNever();
  375. $adapter->reject($exception);
  376. $adapter->promise()
  377. ->otherwise(function (InvalidArgumentException $reason) use ($mock) {
  378. $mock($reason);
  379. })->then(null, $this->expectCallableOnce()); // avoid reporting unhandled rejection
  380. }
  381. /**
  382. * @test
  383. * @deprecated
  384. */
  385. public function alwaysShouldNotSuppressRejectionForRejectedPromise(): void
  386. {
  387. $adapter = $this->getPromiseTestAdapter();
  388. $exception = new Exception();
  389. $mock = $this->createCallableMock();
  390. $mock
  391. ->expects($this->once())
  392. ->method('__invoke')
  393. ->with($this->identicalTo($exception));
  394. $adapter->reject($exception);
  395. $adapter->promise()
  396. ->always(function () {})
  397. ->then(null, $mock);
  398. }
  399. /**
  400. * @test
  401. * @deprecated
  402. */
  403. public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsANonPromiseForRejectedPromise(): void
  404. {
  405. $adapter = $this->getPromiseTestAdapter();
  406. $exception = new Exception();
  407. $mock = $this->createCallableMock();
  408. $mock
  409. ->expects($this->once())
  410. ->method('__invoke')
  411. ->with($this->identicalTo($exception));
  412. $adapter->reject($exception);
  413. $adapter->promise()
  414. ->finally(function (): int { // @phpstan-ignore-line
  415. return 1;
  416. })
  417. ->then(null, $mock);
  418. }
  419. /**
  420. * @test
  421. * @deprecated
  422. */
  423. public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsAPromiseForRejectedPromise(): void
  424. {
  425. $adapter = $this->getPromiseTestAdapter();
  426. $exception = new Exception();
  427. $mock = $this->createCallableMock();
  428. $mock
  429. ->expects($this->once())
  430. ->method('__invoke')
  431. ->with($this->identicalTo($exception));
  432. $adapter->reject($exception);
  433. $adapter->promise()
  434. ->always(function (): PromiseInterface { // @phpstan-ignore-line
  435. return resolve(1);
  436. })
  437. ->then(null, $mock);
  438. }
  439. /**
  440. * @test
  441. * @deprecated
  442. */
  443. public function alwaysShouldRejectWhenHandlerThrowsForRejectedPromise(): void
  444. {
  445. $adapter = $this->getPromiseTestAdapter();
  446. $exception1 = new Exception();
  447. $exception2 = new Exception();
  448. $mock = $this->createCallableMock();
  449. $mock
  450. ->expects($this->once())
  451. ->method('__invoke')
  452. ->with($this->identicalTo($exception2));
  453. $adapter->reject($exception1);
  454. $adapter->promise()
  455. ->always(function () use ($exception2) {
  456. throw $exception2;
  457. })
  458. ->then(null, $mock);
  459. }
  460. /**
  461. * @test
  462. * @deprecated
  463. */
  464. public function alwaysShouldRejectWhenHandlerRejectsForRejectedPromise(): void
  465. {
  466. $adapter = $this->getPromiseTestAdapter();
  467. $exception1 = new Exception();
  468. $exception2 = new Exception();
  469. $mock = $this->createCallableMock();
  470. $mock
  471. ->expects($this->once())
  472. ->method('__invoke')
  473. ->with($this->identicalTo($exception2));
  474. $adapter->reject($exception1);
  475. $adapter->promise()
  476. ->always(function () use ($exception2) {
  477. return reject($exception2);
  478. })
  479. ->then(null, $mock);
  480. }
  481. }