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