1<?php
2
3namespace React\Promise\PromiseTest;
4
5trait PromiseFulfilledTestTrait
6{
7    /**
8     * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface
9     */
10    abstract public function getPromiseTestAdapter(callable $canceller = null);
11
12    /** @test */
13    public function fulfilledPromiseShouldBeImmutable()
14    {
15        $adapter = $this->getPromiseTestAdapter();
16
17        $mock = $this->createCallableMock();
18        $mock
19            ->expects($this->once())
20            ->method('__invoke')
21            ->with($this->identicalTo(1));
22
23        $adapter->resolve(1);
24        $adapter->resolve(2);
25
26        $adapter->promise()
27            ->then(
28                $mock,
29                $this->expectCallableNever()
30            );
31    }
32
33    /** @test */
34    public function fulfilledPromiseShouldInvokeNewlyAddedCallback()
35    {
36        $adapter = $this->getPromiseTestAdapter();
37
38        $adapter->resolve(1);
39
40        $mock = $this->createCallableMock();
41        $mock
42            ->expects($this->once())
43            ->method('__invoke')
44            ->with($this->identicalTo(1));
45
46        $adapter->promise()
47            ->then($mock, $this->expectCallableNever());
48    }
49
50    /** @test */
51    public function thenShouldForwardResultWhenCallbackIsNull()
52    {
53        $adapter = $this->getPromiseTestAdapter();
54
55        $mock = $this->createCallableMock();
56        $mock
57            ->expects($this->once())
58            ->method('__invoke')
59            ->with($this->identicalTo(1));
60
61        $adapter->resolve(1);
62        $adapter->promise()
63            ->then(
64                null,
65                $this->expectCallableNever()
66            )
67            ->then(
68                $mock,
69                $this->expectCallableNever()
70            );
71    }
72
73    /** @test */
74    public function thenShouldForwardCallbackResultToNextCallback()
75    {
76        $adapter = $this->getPromiseTestAdapter();
77
78        $mock = $this->createCallableMock();
79        $mock
80            ->expects($this->once())
81            ->method('__invoke')
82            ->with($this->identicalTo(2));
83
84        $adapter->resolve(1);
85        $adapter->promise()
86            ->then(
87                function ($val) {
88                    return $val + 1;
89                },
90                $this->expectCallableNever()
91            )
92            ->then(
93                $mock,
94                $this->expectCallableNever()
95            );
96    }
97
98    /** @test */
99    public function thenShouldForwardPromisedCallbackResultValueToNextCallback()
100    {
101        $adapter = $this->getPromiseTestAdapter();
102
103        $mock = $this->createCallableMock();
104        $mock
105            ->expects($this->once())
106            ->method('__invoke')
107            ->with($this->identicalTo(2));
108
109        $adapter->resolve(1);
110        $adapter->promise()
111            ->then(
112                function ($val) {
113                    return \React\Promise\resolve($val + 1);
114                },
115                $this->expectCallableNever()
116            )
117            ->then(
118                $mock,
119                $this->expectCallableNever()
120            );
121    }
122
123    /** @test */
124    public function thenShouldSwitchFromCallbacksToErrbacksWhenCallbackReturnsARejection()
125    {
126        $adapter = $this->getPromiseTestAdapter();
127
128        $mock = $this->createCallableMock();
129        $mock
130            ->expects($this->once())
131            ->method('__invoke')
132            ->with($this->identicalTo(2));
133
134        $adapter->resolve(1);
135        $adapter->promise()
136            ->then(
137                function ($val) {
138                    return \React\Promise\reject($val + 1);
139                },
140                $this->expectCallableNever()
141            )
142            ->then(
143                $this->expectCallableNever(),
144                $mock
145            );
146    }
147
148    /** @test */
149    public function thenShouldSwitchFromCallbacksToErrbacksWhenCallbackThrows()
150    {
151        $adapter = $this->getPromiseTestAdapter();
152
153        $exception = new \Exception();
154
155        $mock = $this->createCallableMock();
156        $mock
157            ->expects($this->once())
158            ->method('__invoke')
159            ->will($this->throwException($exception));
160
161        $mock2 = $this->createCallableMock();
162        $mock2
163            ->expects($this->once())
164            ->method('__invoke')
165            ->with($this->identicalTo($exception));
166
167        $adapter->resolve(1);
168        $adapter->promise()
169            ->then(
170                $mock,
171                $this->expectCallableNever()
172            )
173            ->then(
174                $this->expectCallableNever(),
175                $mock2
176            );
177    }
178
179    /** @test */
180    public function cancelShouldReturnNullForFulfilledPromise()
181    {
182        $adapter = $this->getPromiseTestAdapter();
183
184        $adapter->resolve();
185
186        $this->assertNull($adapter->promise()->cancel());
187    }
188
189    /** @test */
190    public function cancelShouldHaveNoEffectForFulfilledPromise()
191    {
192        $adapter = $this->getPromiseTestAdapter($this->expectCallableNever());
193
194        $adapter->resolve();
195
196        $adapter->promise()->cancel();
197    }
198
199    /** @test */
200    public function doneShouldInvokeFulfillmentHandlerForFulfilledPromise()
201    {
202        $adapter = $this->getPromiseTestAdapter();
203
204        $mock = $this->createCallableMock();
205        $mock
206            ->expects($this->once())
207            ->method('__invoke')
208            ->with($this->identicalTo(1));
209
210        $adapter->resolve(1);
211        $this->assertNull($adapter->promise()->done($mock));
212    }
213
214    /** @test */
215    public function doneShouldThrowExceptionThrownFulfillmentHandlerForFulfilledPromise()
216    {
217        $adapter = $this->getPromiseTestAdapter();
218
219        $this->setExpectedException('\Exception', 'UnhandledRejectionException');
220
221        $adapter->resolve(1);
222        $this->assertNull($adapter->promise()->done(function () {
223            throw new \Exception('UnhandledRejectionException');
224        }));
225    }
226
227    /** @test */
228    public function doneShouldThrowUnhandledRejectionExceptionWhenFulfillmentHandlerRejectsForFulfilledPromise()
229    {
230        $adapter = $this->getPromiseTestAdapter();
231
232        $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
233
234        $adapter->resolve(1);
235        $this->assertNull($adapter->promise()->done(function () {
236            return \React\Promise\reject();
237        }));
238    }
239
240    /** @test */
241    public function otherwiseShouldNotInvokeRejectionHandlerForFulfilledPromise()
242    {
243        $adapter = $this->getPromiseTestAdapter();
244
245        $adapter->resolve(1);
246        $adapter->promise()->otherwise($this->expectCallableNever());
247    }
248
249    /** @test */
250    public function alwaysShouldNotSuppressValueForFulfilledPromise()
251    {
252        $adapter = $this->getPromiseTestAdapter();
253
254        $value = new \stdClass();
255
256        $mock = $this->createCallableMock();
257        $mock
258            ->expects($this->once())
259            ->method('__invoke')
260            ->with($this->identicalTo($value));
261
262        $adapter->resolve($value);
263        $adapter->promise()
264            ->always(function () {})
265            ->then($mock);
266    }
267
268    /** @test */
269    public function alwaysShouldNotSuppressValueWhenHandlerReturnsANonPromiseForFulfilledPromise()
270    {
271        $adapter = $this->getPromiseTestAdapter();
272
273        $value = new \stdClass();
274
275        $mock = $this->createCallableMock();
276        $mock
277            ->expects($this->once())
278            ->method('__invoke')
279            ->with($this->identicalTo($value));
280
281        $adapter->resolve($value);
282        $adapter->promise()
283            ->always(function () {
284                return 1;
285            })
286            ->then($mock);
287    }
288
289    /** @test */
290    public function alwaysShouldNotSuppressValueWhenHandlerReturnsAPromiseForFulfilledPromise()
291    {
292        $adapter = $this->getPromiseTestAdapter();
293
294        $value = new \stdClass();
295
296        $mock = $this->createCallableMock();
297        $mock
298            ->expects($this->once())
299            ->method('__invoke')
300            ->with($this->identicalTo($value));
301
302        $adapter->resolve($value);
303        $adapter->promise()
304            ->always(function () {
305                return \React\Promise\resolve(1);
306            })
307            ->then($mock);
308    }
309
310    /** @test */
311    public function alwaysShouldRejectWhenHandlerThrowsForFulfilledPromise()
312    {
313        $adapter = $this->getPromiseTestAdapter();
314
315        $exception = new \Exception();
316
317        $mock = $this->createCallableMock();
318        $mock
319            ->expects($this->once())
320            ->method('__invoke')
321            ->with($this->identicalTo($exception));
322
323        $adapter->resolve(1);
324        $adapter->promise()
325            ->always(function () use ($exception) {
326                throw $exception;
327            })
328            ->then(null, $mock);
329    }
330
331    /** @test */
332    public function alwaysShouldRejectWhenHandlerRejectsForFulfilledPromise()
333    {
334        $adapter = $this->getPromiseTestAdapter();
335
336        $exception = new \Exception();
337
338        $mock = $this->createCallableMock();
339        $mock
340            ->expects($this->once())
341            ->method('__invoke')
342            ->with($this->identicalTo($exception));
343
344        $adapter->resolve(1);
345        $adapter->promise()
346            ->always(function () use ($exception) {
347                return \React\Promise\reject($exception);
348            })
349            ->then(null, $mock);
350    }
351}
352