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