From 6743175a3fd9217814fcc394f18f0ba83fa2b5c9 Mon Sep 17 00:00:00 2001 From: Alex Pott <alex.a.pott@googlemail.com> Date: Sun, 1 Mar 2015 14:11:33 +0000 Subject: [PATCH] Issue #2442411 by jhedstrom: Update react/promise to 2.2.0 --- core/composer.lock | 12 +- core/vendor/composer/autoload_files.php | 2 +- core/vendor/composer/installed.json | 92 +++--- core/vendor/react/promise/CHANGELOG.md | 12 + core/vendor/react/promise/README.md | 279 ++++++++++++++--- core/vendor/react/promise/composer.json | 2 +- core/vendor/react/promise/src/Deferred.php | 23 +- .../promise/src/ExtendedPromiseInterface.php | 26 ++ .../react/promise/src/FulfilledPromise.php | 34 ++- core/vendor/react/promise/src/LazyPromise.php | 23 +- core/vendor/react/promise/src/Promise.php | 67 +++- .../react/promise/src/RejectedPromise.php | 42 ++- .../src/UnhandledRejectionException.php | 31 ++ core/vendor/react/promise/src/functions.php | 56 +++- .../react/promise/src/functions_include.php | 5 + .../react/promise/tests/DeferredTest.php | 29 +- .../promise/tests/FulfilledPromiseTest.php | 4 +- .../tests/FunctionCheckTypehintTest.php | 118 +++++++ .../promise/tests/FunctionResolveTest.php | 8 + .../react/promise/tests/LazyPromiseTest.php | 8 +- .../PromiseAdapter/CallbackPromiseAdapter.php | 4 +- .../PromiseAdapterInterface.php | 2 +- .../react/promise/tests/PromiseTest.php | 74 ++++- .../tests/PromiseTest/FullTestTrait.php | 2 +- ...gressTestTrait.php => NotifyTestTrait.php} | 85 ++++-- .../PromiseTest/PromiseFulfilledTestTrait.php | 153 ++++++++++ .../PromiseTest/PromisePendingTestTrait.php | 33 ++ .../PromiseTest/PromiseRejectedTestTrait.php | 289 ++++++++++++++++++ .../PromiseTest/PromiseSettledTestTrait.php | 37 +++ .../tests/PromiseTest/RejectTestTrait.php | 255 ++++++++++++++++ .../tests/PromiseTest/ResolveTestTrait.php | 149 +++++++++ .../promise/tests/RejectedPromiseTest.php | 4 +- 32 files changed, 1798 insertions(+), 162 deletions(-) create mode 100644 core/vendor/react/promise/src/ExtendedPromiseInterface.php create mode 100644 core/vendor/react/promise/src/UnhandledRejectionException.php create mode 100644 core/vendor/react/promise/src/functions_include.php create mode 100644 core/vendor/react/promise/tests/FunctionCheckTypehintTest.php rename core/vendor/react/promise/tests/PromiseTest/{ProgressTestTrait.php => NotifyTestTrait.php} (73%) diff --git a/core/composer.lock b/core/composer.lock index a8c2d39f6a8e..8fd348dd9d9a 100644 --- a/core/composer.lock +++ b/core/composer.lock @@ -1390,16 +1390,16 @@ }, { "name": "react/promise", - "version": "v2.1.0", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "937b04f1b0ee8f6d180e75a0830aac778ca4bcd6" + "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/937b04f1b0ee8f6d180e75a0830aac778ca4bcd6", - "reference": "937b04f1b0ee8f6d180e75a0830aac778ca4bcd6", + "url": "https://api.github.com/repos/reactphp/promise/zipball/365fcee430dfa4ace1fbc75737ca60ceea7eeeef", + "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef", "shasum": "" }, "require": { @@ -1416,7 +1416,7 @@ "React\\Promise\\": "src/" }, "files": [ - "src/functions.php" + "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1430,7 +1430,7 @@ } ], "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "time": "2014-10-15 20:05:57" + "time": "2014-12-30 13:32:42" }, { "name": "sdboyer/gliph", diff --git a/core/vendor/composer/autoload_files.php b/core/vendor/composer/autoload_files.php index c85d22cbdb20..853f9a068592 100644 --- a/core/vendor/composer/autoload_files.php +++ b/core/vendor/composer/autoload_files.php @@ -6,6 +6,6 @@ $baseDir = dirname($vendorDir); return array( - $vendorDir . '/react/promise/src/functions.php', + $vendorDir . '/react/promise/src/functions_include.php', $baseDir . '/lib/Drupal.php', ); diff --git a/core/vendor/composer/installed.json b/core/vendor/composer/installed.json index d41b956a9fe6..4544688d6f64 100644 --- a/core/vendor/composer/installed.json +++ b/core/vendor/composer/installed.json @@ -329,52 +329,6 @@ "parser" ] }, - { - "name": "react/promise", - "version": "v2.1.0", - "version_normalized": "2.1.0.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "937b04f1b0ee8f6d180e75a0830aac778ca4bcd6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/937b04f1b0ee8f6d180e75a0830aac778ca4bcd6", - "reference": "937b04f1b0ee8f6d180e75a0830aac778ca4bcd6", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "time": "2014-10-15 20:05:57", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "React\\Promise\\": "src/" - }, - "files": [ - "src/functions.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@googlemail.com" - } - ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP" - }, { "name": "guzzlehttp/streams", "version": "3.0.0", @@ -3066,5 +3020,51 @@ "keywords": [ "scraper" ] + }, + { + "name": "react/promise", + "version": "v2.2.0", + "version_normalized": "2.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/365fcee430dfa4ace1fbc75737ca60ceea7eeeef", + "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "time": "2014-12-30 13:32:42", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@googlemail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP" } ] diff --git a/core/vendor/react/promise/CHANGELOG.md b/core/vendor/react/promise/CHANGELOG.md index 870e112e2e42..f2d16543dfb2 100644 --- a/core/vendor/react/promise/CHANGELOG.md +++ b/core/vendor/react/promise/CHANGELOG.md @@ -1,6 +1,18 @@ CHANGELOG ========= +* 2.2.0 (2014-12-30) + + * Introduce new ExtendedPromiseInterface implemented by all promises + * Add new .done() method (part of the ExtendedPromiseInterface) + * Add new .otherwise() method (part of the ExtendedPromiseInterface) + * Add new .always() method (part of the ExtendedPromiseInterface) + * Add new .progress() method (part of the ExtendedPromiseInterface) + * Rename Deferred::progress to Deferred::notify to avoid confusion with + ExtendedPromiseInterface::progress (a Deferred::progress alias is still + available for backward compatibility) + * resolve() now always returns a ExtendedPromiseInterface + * 2.1.0 (2014-10-15) * Introduce new CancellablePromiseInterface implemented by all promises diff --git a/core/vendor/react/promise/README.md b/core/vendor/react/promise/README.md index 1bb5c32c969c..0be869ebc57b 100644 --- a/core/vendor/react/promise/README.md +++ b/core/vendor/react/promise/README.md @@ -18,9 +18,14 @@ Table of Contents * [Deferred::promise()](#deferredpromise) * [Deferred::resolve()](#deferredresolve) * [Deferred::reject()](#deferredreject) - * [Deferred::progress()](#deferredprogress) + * [Deferred::notify()](#deferrednotify) * [PromiseInterface](#promiseinterface) * [PromiseInterface::then()](#promiseinterfacethen) + * [ExtendedPromiseInterface](#extendedpromiseinterface) + * [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone) + * [ExtendedPromiseInterface::otherwise()](#extendedpromiseinterfaceotherwise) + * [ExtendedPromiseInterface::always()](#extendedpromiseinterfacealways) + * [ExtendedPromiseInterface::progress()](#extendedpromiseinterfaceprogress) * [CancellablePromiseInterface](#cancellablepromiseinterface) * [CancellablePromiseInterface::cancel()](#cancellablepromiseinterfacecancel) * [Promise](#promise-1) @@ -44,6 +49,7 @@ Table of Contents * [Rejection forwarding](#rejection-forwarding) * [Mixed resolution and rejection forwarding](#mixed-resolution-and-rejection-forwarding) * [Progress event forwarding](#progress-event-forwarding) + * [done() vs. then()](#done-vs-then) 5. [Credits](#credits) 6. [License](#license) @@ -82,28 +88,28 @@ API A deferred represents an operation whose resolution is pending. It has separate promise and resolver parts. -``` php +```php $deferred = new React\Promise\Deferred(); $promise = $deferred->promise(); $deferred->resolve(mixed $value = null); $deferred->reject(mixed $reason = null); -$deferred->progress(mixed $update = null); +$deferred->notify(mixed $update = null); ``` The `promise` method returns the promise of the deferred. The `resolve` and `reject` methods control the state of the deferred. -The `progress` method is for progress notification. +The `notify` method is for progress notification. The constructor of the `Deferred` accepts an optional `$canceller` argument. See [Promise](#promise-1) for more information. #### Deferred::promise() -``` php +```php $promise = $deferred->promise(); ``` @@ -112,7 +118,7 @@ keeping the authority to modify its state to yourself. #### Deferred::resolve() -``` php +```php $deferred->resolve(mixed $value = null); ``` @@ -125,7 +131,7 @@ this promise once it is resolved. #### Deferred::reject() -``` php +```php $deferred->reject(mixed $reason = null); ``` @@ -137,10 +143,10 @@ All consumers are notified by having `$onRejected` (which they registered via If `$reason` itself is a promise, the promise will be rejected with the outcome of this promise regardless whether it fulfills or rejects. -#### Deferred::progress() +#### Deferred::notify() -``` php -$deferred->progress(mixed $update = null); +```php +$deferred->notify(mixed $update = null); ``` Triggers progress notifications, to indicate to consumers that the computation @@ -160,14 +166,24 @@ and an associated value, or rejection (failure) and an associated reason. Once in the fulfilled or rejected state, a promise becomes immutable. Neither its state nor its result (or error) can be modified. +#### Implementations + +* [Promise](#promise-1) +* [FulfilledPromise](#fulfilledpromise) +* [RejectedPromise](#rejectedpromise) +* [LazyPromise](#lazypromise) + #### PromiseInterface::then() -``` php -$newPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); +```php +$transformedPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); ``` +Transforms a promise's value by applying a function to the promise's fulfillment +or rejection value. Returns a new promise for the transformed result. + The `then()` method registers new fulfilled, rejection and progress handlers -with this promise (all parameters are optional): +with a promise (all parameters are optional): * `$onFulfilled` will be invoked once the promise is fulfilled and passed the result as the first argument. @@ -190,6 +206,18 @@ the same call to `then()`: than once. 3. `$onProgress` may be called multiple times. +#### See also + +* [resolve()](#resolve) - Creating a resolved promise +* [reject()](#reject) - Creating a rejected promise +* [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone) +* [done() vs. then()](#done-vs-then) + +### ExtendedPromiseInterface + +The ExtendedPromiseInterface extends the PromiseInterface with useful shortcut +and utility methods which are not part of the Promises/A specification. + #### Implementations * [Promise](#promise-1) @@ -197,10 +225,110 @@ the same call to `then()`: * [RejectedPromise](#rejectedpromise) * [LazyPromise](#lazypromise) +#### ExtendedPromiseInterface::done() + +```php +$promise->done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); +``` + +Consumes the promise's ultimate value if the promise fulfills, or handles the +ultimate error. + +It will cause a fatal error if either `$onFulfilled` or `$onRejected` throw or +return a rejected promise. + +Since the purpose of `done()` is consumption rather than transformation, +`done()` always returns `null`. + #### See also -* [resolve()](#resolve) - Creating a resolved promise -* [reject()](#reject) - Creating a rejected promise +* [PromiseInterface::then()](#promiseinterfacethen) +* [done() vs. then()](#done-vs-then) + +#### ExtendedPromiseInterface::otherwise() + +```php +$promise->otherwise(callable $onRejected); +``` + +Registers a rejection handler for promise. It is a shortcut for: + +```php +$promise->then(null, $onRejected); +``` + +Additionally, you can type hint the `$reason` argument of `$onRejected` to catch +only specific errors. + +```php +$promise + ->otherwise(function (\RuntimeException $reason) { + // Only catch \RuntimeException instances + // All other types of errors will propagate automatically + }) + ->otherwise(function ($reason) { + // Catch other errors + )}; +``` + +#### ExtendedPromiseInterface::always() + +```php +$newPromise = $promise->always(callable $onFulfilledOrRejected); +``` + +Allows you to execute "cleanup" type tasks in a promise chain. + +It arranges for `$onFulfilledOrRejected` to be called, with no arguments, +when the promise is either fulfilled or rejected. + +* If `$promise` fulfills, and `$onFulfilledOrRejected` returns successfully, + `$newPromise` will fulfill with the same value as `$promise`. +* If `$promise` fulfills, and `$onFulfilledOrRejected` throws or returns a + rejected promise, `$newPromise` will reject with the thrown exception or + rejected promise's reason. +* If `$promise` rejects, and `$onFulfilledOrRejected` returns successfully, + `$newPromise` will reject with the same reason as `$promise`. +* If `$promise` rejects, and `$onFulfilledOrRejected` throws or returns a + rejected promise, `$newPromise` will reject with the thrown exception or + rejected promise's reason. + +`always()` behaves similarly to the synchronous finally statement. When combined +with `otherwise()`, `always()` allows you to write code that is similar to the familiar +synchronous catch/finally pair. + +Consider the following synchronous code: + +```php +try { + return doSomething(); +} catch(\Exception $e) { + return handleError($e); +} finally { + cleanup(); +} +``` + +Similar asynchronous code (with `doSomething()` that returns a promise) can be +written: + +```php +return doSomething() + ->otherwise('handleError') + ->always('cleanup'); +``` + +#### ExtendedPromiseInterface::progress() + +```php +$promise->progress(callable $onProgress); +``` + +Registers a handler for progress updates from promise. It is a shortcut for: + +```php +$promise->then(null, null, $onProgress); +``` ### CancellablePromiseInterface @@ -232,8 +360,8 @@ a promise has no effect. Creates a promise whose state is controlled by the functions passed to `$resolver`. -``` php -$resolver = function (callable $resolve, callable $reject, callable $progress) { +```php +$resolver = function (callable $resolve, callable $reject, callable $notify) { // Do some work, possibly asynchronously, and then // resolve or reject. You can notify of progress events // along the way if you want/need. @@ -241,7 +369,7 @@ $resolver = function (callable $resolve, callable $reject, callable $progress) { $resolve($awesomeResult); // or $resolve($anotherPromise); // or $reject($nastyError); - // or $progress($progressNotification); + // or $notify($progressNotification); }; $canceller = function (callable $resolve, callable $reject, callable $progress) { @@ -262,7 +390,7 @@ function which both will be called with 3 arguments: When called with another promise, e.g. `$resolve($otherPromise)`, promise's fate will be equivalent to that of `$otherPromise`. * `$reject($reason)` - Function that rejects the promise. - * `$progress($update)` - Function that issues progress events for the promise. + * `$notify($update)` - Function that issues progress events for the promise. If the resolver or canceller throw an exception, the promise will be rejected with that thrown exception as the rejection reason. @@ -320,20 +448,25 @@ promises. #### resolve() -``` php +```php $promise = React\Promise\resolve(mixed $promiseOrValue); ``` -Creates a resolved promise for the supplied `$promiseOrValue`. +Creates a promise for the supplied `$promiseOrValue`. If `$promiseOrValue` is a value, it will be the resolution value of the returned promise. If `$promiseOrValue` is a promise, it will simply be returned. +Note: The promise returned is always a promise implementing +[ExtendedPromiseInterface](#extendedpromiseinterface). If you pass in a custom +promise which only implements [PromiseInterface](#promiseinterface), this +promise will be assimilated to a extended promise following `$promiseOrValue`. + #### reject() -``` php +```php $promise = React\Promise\reject(mixed $promiseOrValue); ``` @@ -351,7 +484,7 @@ the value of another promise. #### all() -``` php +```php $promise = React\Promise\all(array|React\Promise\PromiseInterface $promisesOrValues); ``` @@ -362,7 +495,7 @@ will be an array containing the resolution values of each of the items in #### race() -``` php +```php $promise = React\Promise\race(array|React\Promise\PromiseInterface $promisesOrValues); ``` @@ -371,7 +504,7 @@ resolved in the same way the first settled promise resolves. #### any() -``` php +```php $promise = React\Promise\any(array|React\Promise\PromiseInterface $promisesOrValues); ``` @@ -384,7 +517,7 @@ rejected. The rejection value will be an array of all rejection reasons. #### some() -``` php +```php $promise = React\Promise\some(array|React\Promise\PromiseInterface $promisesOrValues, integer $howMany); ``` @@ -400,7 +533,7 @@ reject). The rejection value will be an array of #### map() -``` php +```php $promise = React\Promise\map(array|React\Promise\PromiseInterface $promisesOrValues, callable $mapFunc); ``` @@ -412,7 +545,7 @@ value of a promise or value in `$promisesOrValues`. #### reduce() -``` php +```php $promise = React\Promise\reduce(array|React\Promise\PromiseInterface $promisesOrValues, callable $reduceFunc , $initialValue = null); ``` @@ -432,7 +565,7 @@ Examples ### How to use Deferred -``` php +```php function getAwesomeResultPromise() { $deferred = new React\Promise\Deferred(); @@ -480,7 +613,7 @@ to `$deferred->resolve()` below. Each call to `then()` returns a new promise that will resolve with the return value of the previous handler. This creates a promise "pipeline". -``` php +```php $deferred = new React\Promise\Deferred(); $deferred->promise() @@ -520,7 +653,7 @@ Similarly, when you handle a rejected promise, to propagate the rejection, "rethrow" it by either returning a rejected promise, or actually throwing (since promise translates thrown exceptions into rejections) -``` php +```php $deferred = new React\Promise\Deferred(); $deferred->promise() @@ -547,7 +680,7 @@ $deferred->resolve(1); // Prints "Reject 3" Just like try/catch, you can choose to propagate or not. Mixing resolutions and rejections will still forward handler results in a predictable way. -``` php +```php $deferred = new React\Promise\Deferred(); $deferred->promise() @@ -587,20 +720,94 @@ chain so that they are meaningful to the next step. It also allows you to choose not to transform them, and simply let them propagate untransformed, by not registering a progress handler. -``` php +```php $deferred = new React\Promise\Deferred(); $deferred->promise() - ->then(null, null, function ($update) { + ->progress(function ($update) { return $update + 1; }) - ->then(null, null, function ($update) { + ->progress(function ($update) { echo 'Progress ' . $update; // 2 }); -$deferred->progress(1); // Prints "Progress 2" +$deferred->notify(1); // Prints "Progress 2" ``` +### done() vs. then() + +The golden rule is: + + Either return your promise, or call done() on it. + +At a first glance, `then()` and `done()` seem very similar. However, there are +important distinctions. + +The intent of `then()` is to transform a promise's value and to pass or return +a new promise for the transformed value along to other parts of your code. + +The intent of `done()` is to consume a promise's value, transferring +responsibility for the value to your code. + +In addition to transforming a value, `then()` allows you to recover from, or +propagate intermediate errors. Any errors that are not handled will be caught +by the promise machinery and used to reject the promise returned by `then()`. + +Calling `done()` transfers all responsibility for errors to your code. If an +error (either a thrown exception or returned rejection) escapes the +`$onFulfilled` or `$onRejected` callbacks you provide to done, it will be +rethrown in an uncatchable way causing a fatal error. + +```php +function getJsonResult() +{ + return queryApi() + ->then( + // Transform API results to an object + function ($jsonResultString) { + return json_decode($jsonResultString); + }, + // Transform API errors to an exception + function ($jsonErrorString) { + $object = json_decode($jsonErrorString); + throw new ApiErrorException($object->errorMessage); + } + ); +} + +// Here we provide no rejection handler. +// If the promise returned has been rejected, +// a React\Promise\UnhandledRejectionException will be thrown +getJsonResult() + ->done( + // Consume transformed object + function ($jsonResultObject) { + // Do something with $jsonObject + } + ); + +// Here we provide a rejection handler which will either throw while debugging +// or log the exception. +getJsonResult() + ->done( + function ($jsonObject) { + // Do something with $jsonObject + }, + function (ApiErrorException $exception) { + if (isDebug()) { + throw $e; + } else { + logException($exception); + } + } + ); +``` + +Note that if a rejection value is not an instance of `\Exception`, it will be +wrapped in an exception of the type `React\Promise\UnhandledRejectionException`. + +You can get the original rejection reason by calling `$exception->getReason()`. + Credits ------- diff --git a/core/vendor/react/promise/composer.json b/core/vendor/react/promise/composer.json index b8ca9a5450c3..c428b7ba6558 100644 --- a/core/vendor/react/promise/composer.json +++ b/core/vendor/react/promise/composer.json @@ -12,7 +12,7 @@ "psr-4": { "React\\Promise\\": "src/" }, - "files": ["src/functions.php"] + "files": ["src/functions_include.php"] }, "extra": { "branch-alias": { diff --git a/core/vendor/react/promise/src/Deferred.php b/core/vendor/react/promise/src/Deferred.php index d7ae0aa1039b..f23980c31184 100644 --- a/core/vendor/react/promise/src/Deferred.php +++ b/core/vendor/react/promise/src/Deferred.php @@ -7,7 +7,7 @@ class Deferred implements PromisorInterface private $promise; private $resolveCallback; private $rejectCallback; - private $progressCallback; + private $notifyCallback; private $canceller; public function __construct(callable $canceller = null) @@ -18,10 +18,10 @@ public function __construct(callable $canceller = null) public function promise() { if (null === $this->promise) { - $this->promise = new Promise(function ($resolve, $reject, $progress) { - $this->resolveCallback = $resolve; - $this->rejectCallback = $reject; - $this->progressCallback = $progress; + $this->promise = new Promise(function ($resolve, $reject, $notify) { + $this->resolveCallback = $resolve; + $this->rejectCallback = $reject; + $this->notifyCallback = $notify; }, $this->canceller); } @@ -42,10 +42,19 @@ public function reject($reason = null) call_user_func($this->rejectCallback, $reason); } - public function progress($update = null) + public function notify($update = null) { $this->promise(); - call_user_func($this->progressCallback, $update); + call_user_func($this->notifyCallback, $update); + } + + /** + * @deprecated 2.2.0 + * @see Deferred::notify() + */ + public function progress($update = null) + { + $this->notify($update); } } diff --git a/core/vendor/react/promise/src/ExtendedPromiseInterface.php b/core/vendor/react/promise/src/ExtendedPromiseInterface.php new file mode 100644 index 000000000000..9cb643598752 --- /dev/null +++ b/core/vendor/react/promise/src/ExtendedPromiseInterface.php @@ -0,0 +1,26 @@ +<?php + +namespace React\Promise; + +interface ExtendedPromiseInterface extends PromiseInterface +{ + /** + * @return void + */ + public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); + + /** + * @return ExtendedPromiseInterface + */ + public function otherwise(callable $onRejected); + + /** + * @return ExtendedPromiseInterface + */ + public function always(callable $onFulfilledOrRejected); + + /** + * @return ExtendedPromiseInterface + */ + public function progress(callable $onProgress); +} diff --git a/core/vendor/react/promise/src/FulfilledPromise.php b/core/vendor/react/promise/src/FulfilledPromise.php index bfa3f9953e80..cd3665929bb5 100644 --- a/core/vendor/react/promise/src/FulfilledPromise.php +++ b/core/vendor/react/promise/src/FulfilledPromise.php @@ -2,7 +2,7 @@ namespace React\Promise; -class FulfilledPromise implements CancellablePromiseInterface +class FulfilledPromise implements ExtendedPromiseInterface, CancellablePromiseInterface { private $value; @@ -30,6 +30,38 @@ public function then(callable $onFulfilled = null, callable $onRejected = null, } } + public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + if (null === $onFulfilled) { + return; + } + + $result = $onFulfilled($this->value); + + if ($result instanceof ExtendedPromiseInterface) { + $result->done(); + } + } + + public function otherwise(callable $onRejected) + { + return new FulfilledPromise($this->value); + } + + public function always(callable $onFulfilledOrRejected) + { + return $this->then(function ($value) use ($onFulfilledOrRejected) { + return resolve($onFulfilledOrRejected())->then(function () use ($value) { + return $value; + }); + }); + } + + public function progress(callable $onProgress) + { + return new FulfilledPromise($this->value); + } + public function cancel() { } diff --git a/core/vendor/react/promise/src/LazyPromise.php b/core/vendor/react/promise/src/LazyPromise.php index e1172e5499fc..68524a929d9e 100644 --- a/core/vendor/react/promise/src/LazyPromise.php +++ b/core/vendor/react/promise/src/LazyPromise.php @@ -2,7 +2,7 @@ namespace React\Promise; -class LazyPromise implements CancellablePromiseInterface +class LazyPromise implements ExtendedPromiseInterface, CancellablePromiseInterface { private $factory; private $promise; @@ -17,6 +17,27 @@ public function then(callable $onFulfilled = null, callable $onRejected = null, return $this->promise()->then($onFulfilled, $onRejected, $onProgress); } + + public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + return $this->promise()->done($onFulfilled, $onRejected, $onProgress); + } + + public function otherwise(callable $onRejected) + { + return $this->promise()->otherwise($onRejected); + } + + public function always(callable $onFulfilledOrRejected) + { + return $this->promise()->always($onFulfilledOrRejected); + } + + public function progress(callable $onProgress) + { + return $this->promise()->progress($onProgress); + } + public function cancel() { return $this->promise()->cancel(); diff --git a/core/vendor/react/promise/src/Promise.php b/core/vendor/react/promise/src/Promise.php index 09ebb32be831..b2635bf307f3 100644 --- a/core/vendor/react/promise/src/Promise.php +++ b/core/vendor/react/promise/src/Promise.php @@ -2,7 +2,7 @@ namespace React\Promise; -class Promise implements CancellablePromiseInterface +class Promise implements ExtendedPromiseInterface, CancellablePromiseInterface { private $canceller; private $result; @@ -40,6 +40,51 @@ public function then(callable $onFulfilled = null, callable $onRejected = null, }); } + public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + if (null !== $this->result) { + return $this->result->done($onFulfilled, $onRejected, $onProgress); + } + + $this->handlers[] = function (PromiseInterface $promise) use ($onFulfilled, $onRejected) { + $promise + ->done($onFulfilled, $onRejected); + }; + + if ($onProgress) { + $this->progressHandlers[] = $onProgress; + } + } + + public function otherwise(callable $onRejected) + { + return $this->then(null, function ($reason) use ($onRejected) { + if (!_checkTypehint($onRejected, $reason)) { + return new RejectedPromise($reason); + } + + return $onRejected($reason); + }); + } + + public function always(callable $onFulfilledOrRejected) + { + return $this->then(function ($value) use ($onFulfilledOrRejected) { + return resolve($onFulfilledOrRejected())->then(function () use ($value) { + return $value; + }); + }, function ($reason) use ($onFulfilledOrRejected) { + return resolve($onFulfilledOrRejected())->then(function () use ($reason) { + return new RejectedPromise($reason); + }); + }); + } + + public function progress(callable $onProgress) + { + return $this->then(null, null, $onProgress); + } + public function cancel() { if (null === $this->canceller || null !== $this->result) { @@ -51,23 +96,23 @@ public function cancel() private function resolver(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) { - return function ($resolve, $reject, $progress) use ($onFulfilled, $onRejected, $onProgress) { + return function ($resolve, $reject, $notify) use ($onFulfilled, $onRejected, $onProgress) { if ($onProgress) { - $progressHandler = function ($update) use ($progress, $onProgress) { + $progressHandler = function ($update) use ($notify, $onProgress) { try { - $progress($onProgress($update)); + $notify($onProgress($update)); } catch (\Exception $e) { - $progress($e); + $notify($e); } }; } else { - $progressHandler = $progress; + $progressHandler = $notify; } $this->handlers[] = function (PromiseInterface $promise) use ($onFulfilled, $onRejected, $resolve, $reject, $progressHandler) { $promise ->then($onFulfilled, $onRejected) - ->then($resolve, $reject, $progressHandler); + ->done($resolve, $reject, $progressHandler); }; $this->progressHandlers[] = $progressHandler; @@ -92,7 +137,7 @@ private function reject($reason = null) $this->settle(reject($reason)); } - private function progress($update = null) + private function notify($update = null) { if (null !== $this->result) { return; @@ -103,8 +148,10 @@ private function progress($update = null) } } - private function settle(PromiseInterface $result) + private function settle(ExtendedPromiseInterface $promise) { + $result = $promise; + foreach ($this->handlers as $handler) { $handler($result); } @@ -125,7 +172,7 @@ function ($reason = null) { $this->reject($reason); }, function ($update = null) { - $this->progress($update); + $this->notify($update); } ); } catch (\Exception $e) { diff --git a/core/vendor/react/promise/src/RejectedPromise.php b/core/vendor/react/promise/src/RejectedPromise.php index ee318cbf448a..350286df4300 100644 --- a/core/vendor/react/promise/src/RejectedPromise.php +++ b/core/vendor/react/promise/src/RejectedPromise.php @@ -2,7 +2,7 @@ namespace React\Promise; -class RejectedPromise implements CancellablePromiseInterface +class RejectedPromise implements ExtendedPromiseInterface, CancellablePromiseInterface { private $reason; @@ -28,6 +28,46 @@ public function then(callable $onFulfilled = null, callable $onRejected = null, } } + public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + if (null === $onRejected) { + throw UnhandledRejectionException::resolve($this->reason); + } + + $result = $onRejected($this->reason); + + if ($result instanceof self) { + throw UnhandledRejectionException::resolve($result->reason); + } + + if ($result instanceof ExtendedPromiseInterface) { + $result->done(); + } + } + + public function otherwise(callable $onRejected) + { + if (!_checkTypehint($onRejected, $this->reason)) { + return new RejectedPromise($this->reason); + } + + return $this->then(null, $onRejected); + } + + public function always(callable $onFulfilledOrRejected) + { + return $this->then(null, function ($reason) use ($onFulfilledOrRejected) { + return resolve($onFulfilledOrRejected())->then(function () use ($reason) { + return new RejectedPromise($reason); + }); + }); + } + + public function progress(callable $onProgress) + { + return new RejectedPromise($this->reason); + } + public function cancel() { } diff --git a/core/vendor/react/promise/src/UnhandledRejectionException.php b/core/vendor/react/promise/src/UnhandledRejectionException.php new file mode 100644 index 000000000000..ed166b30a53a --- /dev/null +++ b/core/vendor/react/promise/src/UnhandledRejectionException.php @@ -0,0 +1,31 @@ +<?php + +namespace React\Promise; + +class UnhandledRejectionException extends \RuntimeException +{ + private $reason; + + public static function resolve($reason) + { + if ($reason instanceof \Exception) { + return $reason; + } + + return new static($reason); + } + + public function __construct($reason) + { + $this->reason = $reason; + + $message = sprintf('Unhandled Rejection: %s', json_encode($reason)); + + parent::__construct($message, 0); + } + + public function getReason() + { + return $this->reason; + } +} diff --git a/core/vendor/react/promise/src/functions.php b/core/vendor/react/promise/src/functions.php index fbf7766262c3..2eae6050e146 100644 --- a/core/vendor/react/promise/src/functions.php +++ b/core/vendor/react/promise/src/functions.php @@ -4,17 +4,23 @@ function resolve($promiseOrValue = null) { - if ($promiseOrValue instanceof PromiseInterface) { + if (!$promiseOrValue instanceof PromiseInterface) { + return new FulfilledPromise($promiseOrValue); + } + + if ($promiseOrValue instanceof ExtendedPromiseInterface) { return $promiseOrValue; } - return new FulfilledPromise($promiseOrValue); + return new Promise(function ($resolve, $reject, $notify) use ($promiseOrValue) { + $promiseOrValue->then($resolve, $reject, $notify); + }); } function reject($promiseOrValue = null) { if ($promiseOrValue instanceof PromiseInterface) { - return $promiseOrValue->then(function ($value) { + return resolve($promiseOrValue)->then(function ($value) { return new RejectedPromise($value); }); } @@ -37,10 +43,10 @@ function race($promisesOrValues) return resolve(); } - return new Promise(function ($resolve, $reject, $progress) use ($array) { + return new Promise(function ($resolve, $reject, $notify) use ($array) { foreach ($array as $promiseOrValue) { resolve($promiseOrValue) - ->then($resolve, $reject, $progress); + ->done($resolve, $reject, $notify); } }); }); @@ -62,7 +68,7 @@ function some($promisesOrValues, $howMany) return resolve([]); } - return new Promise(function ($resolve, $reject, $progress) use ($array, $howMany) { + return new Promise(function ($resolve, $reject, $notify) use ($array, $howMany) { $len = count($array); $toResolve = min($howMany, $len); $toReject = ($len - $toResolve) + 1; @@ -95,7 +101,7 @@ function some($promisesOrValues, $howMany) }; resolve($promiseOrValue) - ->then($fulfiller, $rejecter, $progress); + ->done($fulfiller, $rejecter, $notify); } }); }); @@ -109,14 +115,14 @@ function map($promisesOrValues, callable $mapFunc) return resolve([]); } - return new Promise(function ($resolve, $reject, $progress) use ($array, $mapFunc) { + return new Promise(function ($resolve, $reject, $notify) use ($array, $mapFunc) { $toResolve = count($array); $values = []; foreach ($array as $i => $promiseOrValue) { resolve($promiseOrValue) ->then($mapFunc) - ->then( + ->done( function ($mapped) use ($i, &$values, &$toResolve, $resolve) { $values[$i] = $mapped; @@ -125,7 +131,7 @@ function ($mapped) use ($i, &$values, &$toResolve, $resolve) { } }, $reject, - $progress + $notify ); } }); @@ -158,3 +164,33 @@ function reduce($promisesOrValues, callable $reduceFunc , $initialValue = null) return array_reduce($array, $wrappedReduceFunc, $initialValue); }); } + +// Internal functions +function _checkTypehint(callable $callback, $object) +{ + if (!is_object($object)) { + return true; + } + + if (is_array($callback)) { + $callbackReflection = new \ReflectionMethod($callback[0], $callback[1]); + } elseif (is_object($callback) && !$callback instanceof \Closure) { + $callbackReflection = new \ReflectionMethod($callback, '__invoke'); + } else { + $callbackReflection = new \ReflectionFunction($callback); + } + + $parameters = $callbackReflection->getParameters(); + + if (!isset($parameters[0])) { + return true; + } + + $expectedException = $parameters[0]; + + if (!$expectedException->getClass()) { + return true; + } + + return $expectedException->getClass()->isInstance($object); +} diff --git a/core/vendor/react/promise/src/functions_include.php b/core/vendor/react/promise/src/functions_include.php new file mode 100644 index 000000000000..c71decbf9895 --- /dev/null +++ b/core/vendor/react/promise/src/functions_include.php @@ -0,0 +1,5 @@ +<?php + +if (!function_exists('React\Promise\resolve')) { + require __DIR__.'/functions.php'; +} diff --git a/core/vendor/react/promise/tests/DeferredTest.php b/core/vendor/react/promise/tests/DeferredTest.php index 754a84dfcadf..16212e9e1cb8 100644 --- a/core/vendor/react/promise/tests/DeferredTest.php +++ b/core/vendor/react/promise/tests/DeferredTest.php @@ -13,11 +13,30 @@ public function getPromiseTestAdapter(callable $canceller = null) $d = new Deferred($canceller); return new CallbackPromiseAdapter([ - 'promise' => [$d, 'promise'], - 'resolve' => [$d, 'resolve'], - 'reject' => [$d, 'reject'], - 'progress' => [$d, 'progress'], - 'settle' => [$d, 'resolve'], + 'promise' => [$d, 'promise'], + 'resolve' => [$d, 'resolve'], + 'reject' => [$d, 'reject'], + 'notify' => [$d, 'progress'], + 'settle' => [$d, 'resolve'], ]); } + + /** @test */ + public function progressIsAnAliasForNotify() + { + $deferred = new Deferred(); + + $sentinel = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($sentinel); + + $deferred->promise() + ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock); + + $deferred->progress($sentinel); + } } diff --git a/core/vendor/react/promise/tests/FulfilledPromiseTest.php b/core/vendor/react/promise/tests/FulfilledPromiseTest.php index e72ecebc0ad0..97fc8f6c6d3c 100644 --- a/core/vendor/react/promise/tests/FulfilledPromiseTest.php +++ b/core/vendor/react/promise/tests/FulfilledPromiseTest.php @@ -29,8 +29,8 @@ public function getPromiseTestAdapter(callable $canceller = null) 'reject' => function () { throw new \LogicException('You cannot call reject() for React\Promise\FulfilledPromise'); }, - 'progress' => function () { - throw new \LogicException('You cannot call progress() for React\Promise\FulfilledPromise'); + 'notify' => function () { + // no-op }, 'settle' => function ($value = null) use (&$promise) { if (!$promise) { diff --git a/core/vendor/react/promise/tests/FunctionCheckTypehintTest.php b/core/vendor/react/promise/tests/FunctionCheckTypehintTest.php new file mode 100644 index 000000000000..8449bc1f7a39 --- /dev/null +++ b/core/vendor/react/promise/tests/FunctionCheckTypehintTest.php @@ -0,0 +1,118 @@ +<?php + +namespace React\Promise; + +class FunctionCheckTypehintTest extends TestCase +{ + /** @test */ + public function shouldAcceptClosureCallbackWithTypehint() + { + $this->assertTrue(_checkTypehint(function (\InvalidArgumentException $e) { + }, new \InvalidArgumentException())); + $this->assertfalse(_checkTypehint(function (\InvalidArgumentException $e) { + }, new \Exception())); + } + + /** @test */ + public function shouldAcceptFunctionStringCallbackWithTypehint() + { + $this->assertTrue(_checkTypehint('React\Promise\testCallbackWithTypehint', new \InvalidArgumentException())); + $this->assertfalse(_checkTypehint('React\Promise\testCallbackWithTypehint', new \Exception())); + } + + /** @test */ + public function shouldAcceptInvokableObjectCallbackWithTypehint() + { + $this->assertTrue(_checkTypehint(new TestCallbackWithTypehintClass(), new \InvalidArgumentException())); + $this->assertfalse(_checkTypehint(new TestCallbackWithTypehintClass(), new \Exception())); + } + + /** @test */ + public function shouldAcceptObjectMethodCallbackWithTypehint() + { + $this->assertTrue(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new \InvalidArgumentException())); + $this->assertfalse(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new \Exception())); + } + + /** @test */ + public function shouldAcceptStaticClassCallbackWithTypehint() + { + $this->assertTrue(_checkTypehint(['React\Promise\TestCallbackWithTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException())); + $this->assertfalse(_checkTypehint(['React\Promise\TestCallbackWithTypehintClass', 'testCallbackStatic'], new \Exception())); + } + + /** @test */ + public function shouldAcceptClosureCallbackWithoutTypehint() + { + $this->assertTrue(_checkTypehint(function (\InvalidArgumentException $e) { + }, new \InvalidArgumentException())); + } + + /** @test */ + public function shouldAcceptFunctionStringCallbackWithoutTypehint() + { + $this->assertTrue(_checkTypehint('React\Promise\testCallbackWithoutTypehint', new \InvalidArgumentException())); + } + + /** @test */ + public function shouldAcceptInvokableObjectCallbackWithoutTypehint() + { + $this->assertTrue(_checkTypehint(new TestCallbackWithoutTypehintClass(), new \InvalidArgumentException())); + } + + /** @test */ + public function shouldAcceptObjectMethodCallbackWithoutTypehint() + { + $this->assertTrue(_checkTypehint([new TestCallbackWithoutTypehintClass(), 'testCallback'], new \InvalidArgumentException())); + } + + /** @test */ + public function shouldAcceptStaticClassCallbackWithoutTypehint() + { + $this->assertTrue(_checkTypehint(['React\Promise\TestCallbackWithoutTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException())); + } +} + +function testCallbackWithTypehint(\InvalidArgumentException $e) +{ +} + +function testCallbackWithoutTypehint() +{ +} + +class TestCallbackWithTypehintClass +{ + public function __invoke(\InvalidArgumentException $e) + { + + } + + public function testCallback(\InvalidArgumentException $e) + { + + } + + public static function testCallbackStatic(\InvalidArgumentException $e) + { + + } +} + +class TestCallbackWithoutTypehintClass +{ + public function __invoke() + { + + } + + public function testCallback() + { + + } + + public static function testCallbackStatic() + { + + } +} diff --git a/core/vendor/react/promise/tests/FunctionResolveTest.php b/core/vendor/react/promise/tests/FunctionResolveTest.php index 01307d5a857a..576c30931f01 100644 --- a/core/vendor/react/promise/tests/FunctionResolveTest.php +++ b/core/vendor/react/promise/tests/FunctionResolveTest.php @@ -91,4 +91,12 @@ function ($val) { $result->then($mock); } + + /** @test */ + public function returnsExtendePromiseForSimplePromise() + { + $promise = $this->getMock('React\Promise\PromiseInterface'); + + $this->assertInstanceOf('React\Promise\ExtendedPromiseInterface', resolve($promise)); + } } diff --git a/core/vendor/react/promise/tests/LazyPromiseTest.php b/core/vendor/react/promise/tests/LazyPromiseTest.php index a6eb44638083..b6308818a771 100644 --- a/core/vendor/react/promise/tests/LazyPromiseTest.php +++ b/core/vendor/react/promise/tests/LazyPromiseTest.php @@ -20,10 +20,10 @@ public function getPromiseTestAdapter(callable $canceller = null) 'promise' => function () use ($factory) { return new LazyPromise($factory); }, - 'resolve' => [$d, 'resolve'], - 'reject' => [$d, 'reject'], - 'progress' => [$d, 'progress'], - 'settle' => [$d, 'resolve'], + 'resolve' => [$d, 'resolve'], + 'reject' => [$d, 'reject'], + 'notify' => [$d, 'progress'], + 'settle' => [$d, 'resolve'], ]); } diff --git a/core/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php b/core/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php index 3a46050e9f43..bdedf4658e45 100644 --- a/core/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php +++ b/core/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php @@ -28,9 +28,9 @@ public function reject() return call_user_func_array($this->callbacks['reject'], func_get_args()); } - public function progress() + public function notify() { - return call_user_func_array($this->callbacks['progress'], func_get_args()); + return call_user_func_array($this->callbacks['notify'], func_get_args()); } public function settle() diff --git a/core/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php b/core/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php index 01baf4667866..9157cd4ea725 100644 --- a/core/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php +++ b/core/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php @@ -9,6 +9,6 @@ interface PromiseAdapterInterface public function promise(); public function resolve(); public function reject(); - public function progress(); + public function notify(); public function settle(); } diff --git a/core/vendor/react/promise/tests/PromiseTest.php b/core/vendor/react/promise/tests/PromiseTest.php index 18e30f7c38ef..faba7046839c 100644 --- a/core/vendor/react/promise/tests/PromiseTest.php +++ b/core/vendor/react/promise/tests/PromiseTest.php @@ -22,10 +22,10 @@ public function getPromiseTestAdapter(callable $canceller = null) 'promise' => function () use ($promise) { return $promise; }, - 'resolve' => $resolveCallback, - 'reject' => $rejectCallback, - 'progress' => $progressCallback, - 'settle' => $resolveCallback, + 'resolve' => $resolveCallback, + 'reject' => $rejectCallback, + 'notify' => $progressCallback, + 'settle' => $resolveCallback, ]); } @@ -47,4 +47,70 @@ public function shouldRejectIfResolverThrowsException() $promise ->then($this->expectCallableNever(), $mock); } + + /** @test */ + public function shouldFulfillIfFullfilledWithSimplePromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo('foo')); + + $adapter->promise() + ->then($mock); + + $adapter->resolve(new SimpleFulfilledTestPromise()); + } + + /** @test */ + public function shouldRejectIfRejectedWithSimplePromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo('foo')); + + $adapter->promise() + ->then($this->expectCallableNever(), $mock); + + $adapter->resolve(new SimpleRejectedTestPromise()); + } +} + +class SimpleFulfilledTestPromise implements PromiseInterface +{ + public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + try { + if ($onFulfilled) { + $onFulfilled('foo'); + } + + return new self('foo'); + } catch (\Exception $exception) { + return new RejectedPromise($exception); + } + } +} + +class SimpleRejectedTestPromise implements PromiseInterface +{ + public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + try { + if ($onRejected) { + $onRejected('foo'); + } + + return new self('foo'); + } catch (\Exception $exception) { + return new RejectedPromise($exception); + } + } } diff --git a/core/vendor/react/promise/tests/PromiseTest/FullTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/FullTestTrait.php index fd8f9342004b..3ce45d61fae4 100644 --- a/core/vendor/react/promise/tests/PromiseTest/FullTestTrait.php +++ b/core/vendor/react/promise/tests/PromiseTest/FullTestTrait.php @@ -10,6 +10,6 @@ trait FullTestTrait PromiseRejectedTestTrait, ResolveTestTrait, RejectTestTrait, - ProgressTestTrait, + NotifyTestTrait, CancelTestTrait; } diff --git a/core/vendor/react/promise/tests/PromiseTest/ProgressTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/NotifyTestTrait.php similarity index 73% rename from core/vendor/react/promise/tests/PromiseTest/ProgressTestTrait.php rename to core/vendor/react/promise/tests/PromiseTest/NotifyTestTrait.php index 0d160a8165aa..4501df67683f 100644 --- a/core/vendor/react/promise/tests/PromiseTest/ProgressTestTrait.php +++ b/core/vendor/react/promise/tests/PromiseTest/NotifyTestTrait.php @@ -2,7 +2,7 @@ namespace React\Promise\PromiseTest; -trait ProgressTestTrait +trait NotifyTestTrait { /** * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface @@ -10,7 +10,7 @@ trait ProgressTestTrait abstract public function getPromiseTestAdapter(callable $canceller = null); /** @test */ - public function progressShouldProgress() + public function notifyShouldProgress() { $adapter = $this->getPromiseTestAdapter(); @@ -25,11 +25,11 @@ public function progressShouldProgress() $adapter->promise() ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock); - $adapter->progress($sentinel); + $adapter->notify($sentinel); } /** @test */ - public function progressShouldPropagateProgressToDownstreamPromises() + public function notifyShouldPropagateProgressToDownstreamPromises() { $adapter = $this->getPromiseTestAdapter(); @@ -59,11 +59,11 @@ public function progressShouldPropagateProgressToDownstreamPromises() $mock2 ); - $adapter->progress($sentinel); + $adapter->notify($sentinel); } /** @test */ - public function progressShouldPropagateTransformedProgressToDownstreamPromises() + public function notifyShouldPropagateTransformedProgressToDownstreamPromises() { $adapter = $this->getPromiseTestAdapter(); @@ -93,11 +93,11 @@ public function progressShouldPropagateTransformedProgressToDownstreamPromises() $mock2 ); - $adapter->progress(1); + $adapter->notify(1); } /** @test */ - public function progressShouldPropagateCaughtExceptionValueAsProgress() + public function notifyShouldPropagateCaughtExceptionValueAsProgress() { $adapter = $this->getPromiseTestAdapter(); @@ -127,11 +127,11 @@ public function progressShouldPropagateCaughtExceptionValueAsProgress() $mock2 ); - $adapter->progress(1); + $adapter->notify(1); } /** @test */ - public function progressShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAResolvedPromiseReturnsAPromise() + public function notifyShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAResolvedPromiseReturnsAPromise() { $adapter = $this->getPromiseTestAdapter(); $adapter2 = $this->getPromiseTestAdapter(); @@ -159,11 +159,11 @@ public function progressShouldForwardProgressEventsWhenIntermediaryCallbackTiedT $mock ); - $adapter2->progress($sentinel); + $adapter2->notify($sentinel); } /** @test */ - public function progressShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAnUnresolvedPromiseReturnsAPromise() + public function notifyShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAnUnresolvedPromiseReturnsAPromise() { $adapter = $this->getPromiseTestAdapter(); $adapter2 = $this->getPromiseTestAdapter(); @@ -190,11 +190,11 @@ public function progressShouldForwardProgressEventsWhenIntermediaryCallbackTiedT // resolve AFTER attaching progress handler $adapter->resolve(); - $adapter2->progress($sentinel); + $adapter2->notify($sentinel); } /** @test */ - public function progressShouldForwardProgressWhenResolvedWithAnotherPromise() + public function notifyShouldForwardProgressWhenResolvedWithAnotherPromise() { $adapter = $this->getPromiseTestAdapter(); $adapter2 = $this->getPromiseTestAdapter(); @@ -226,11 +226,11 @@ public function progressShouldForwardProgressWhenResolvedWithAnotherPromise() ); $adapter->resolve($adapter2->promise()); - $adapter2->progress($sentinel); + $adapter2->notify($sentinel); } /** @test */ - public function progressShouldAllowResolveAfterProgress() + public function notifyShouldAllowResolveAfterProgress() { $adapter = $this->getPromiseTestAdapter(); @@ -251,12 +251,12 @@ public function progressShouldAllowResolveAfterProgress() $mock ); - $adapter->progress(1); + $adapter->notify(1); $adapter->resolve(2); } /** @test */ - public function progressShouldAllowRejectAfterProgress() + public function notifyShouldAllowRejectAfterProgress() { $adapter = $this->getPromiseTestAdapter(); @@ -277,17 +277,60 @@ public function progressShouldAllowRejectAfterProgress() $mock ); - $adapter->progress(1); + $adapter->notify(1); $adapter->reject(2); } /** @test */ - public function progressShouldReturnSilentlyOnProgressWhenAlreadyRejected() + public function notifyShouldReturnSilentlyOnProgressWhenAlreadyRejected() { $adapter = $this->getPromiseTestAdapter(); $adapter->reject(1); - $this->assertNull($adapter->progress()); + $this->assertNull($adapter->notify()); + } + + /** @test */ + public function notifyShouldInvokeProgressHandler() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise()->progress($mock); + $adapter->notify(1); + } + + /** @test */ + public function notifyShouldInvokeProgressHandlerFromDone() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $this->assertNull($adapter->promise()->done(null, null, $mock)); + $adapter->notify(1); + } + + /** @test */ + public function notifyShouldThrowExceptionThrownProgressHandlerFromDone() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done(null, null, function () { + throw new \Exception('UnhandledRejectionException'); + })); + $adapter->notify(1); } } diff --git a/core/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php index 5ae29422d056..428230b97ac1 100644 --- a/core/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php +++ b/core/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php @@ -195,4 +195,157 @@ public function cancelShouldHaveNoEffectForFulfilledPromise() $adapter->promise()->cancel(); } + + /** @test */ + public function doneShouldInvokeFulfillmentHandlerForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->resolve(1); + $this->assertNull($adapter->promise()->done($mock)); + } + + /** @test */ + public function doneShouldThrowExceptionThrownFulfillmentHandlerForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $adapter->resolve(1); + $this->assertNull($adapter->promise()->done(function () { + throw new \Exception('UnhandledRejectionException'); + })); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenFulfillmentHandlerRejectsForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $adapter->resolve(1); + $this->assertNull($adapter->promise()->done(function () { + return \React\Promise\reject(); + })); + } + + /** @test */ + public function otherwiseShouldNotInvokeRejectionHandlerForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->resolve(1); + $adapter->promise()->otherwise($this->expectCallableNever()); + } + + /** @test */ + public function alwaysShouldNotSuppressValueForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $value = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($value)); + + $adapter->resolve($value); + $adapter->promise() + ->always(function () {}) + ->then($mock); + } + + /** @test */ + public function alwaysShouldNotSuppressValueWhenHandlerReturnsANonPromiseForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $value = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($value)); + + $adapter->resolve($value); + $adapter->promise() + ->always(function () { + return 1; + }) + ->then($mock); + } + + /** @test */ + public function alwaysShouldNotSuppressValueWhenHandlerReturnsAPromiseForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $value = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($value)); + + $adapter->resolve($value); + $adapter->promise() + ->always(function () { + return \React\Promise\resolve(1); + }) + ->then($mock); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerThrowsForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->resolve(1); + $adapter->promise() + ->always(function () use ($exception) { + throw $exception; + }) + ->then(null, $mock); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerRejectsForFulfilledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->resolve(1); + $adapter->promise() + ->always(function () use ($exception) { + return \React\Promise\reject($exception); + }) + ->then(null, $mock); + } } diff --git a/core/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php index 0b188013fe90..a4f48ee25304 100644 --- a/core/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php +++ b/core/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php @@ -32,4 +32,37 @@ public function cancelShouldReturnNullForPendingPromise() $this->assertNull($adapter->promise()->cancel()); } + + /** @test */ + public function doneShouldReturnNullForPendingPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->assertNull($adapter->promise()->done()); + } + + /** @test */ + public function doneShouldReturnAllowNullForPendingPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->assertNull($adapter->promise()->done(null, null, null)); + } + + /** @test */ + public function otherwiseShouldNotInvokeRejectionHandlerForPendingPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->settle(); + $adapter->promise()->otherwise($this->expectCallableNever()); + } + + /** @test */ + public function alwaysShouldReturnAPromiseForPendingPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->always(function () {})); + } } diff --git a/core/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php index a9cfa68ad089..64255e8311ab 100644 --- a/core/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php +++ b/core/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php @@ -2,6 +2,8 @@ namespace React\Promise\PromiseTest; +use React\Promise\Deferred; + trait PromiseRejectedTestTrait { /** @@ -181,6 +183,293 @@ function ($val) { ); } + /** @test */ + public function doneShouldInvokeRejectionHandlerForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->reject(1); + $this->assertNull($adapter->promise()->done(null, $mock)); + } + + /** @test */ + public function doneShouldThrowExceptionThrownByRejectionHandlerForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $adapter->reject(1); + $this->assertNull($adapter->promise()->done(null, function () { + throw new \Exception('UnhandledRejectionException'); + })); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenRejectedWithNonExceptionForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $adapter->reject(1); + $this->assertNull($adapter->promise()->done()); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRejectsForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $adapter->reject(1); + $this->assertNull($adapter->promise()->done(null, function () { + return \React\Promise\reject(); + })); + } + + /** @test */ + public function doneShouldThrowRejectionExceptionWhenRejectionHandlerRejectsWithExceptionForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $adapter->reject(1); + $this->assertNull($adapter->promise()->done(null, function () { + return \React\Promise\reject(new \Exception('UnhandledRejectionException')); + })); + } + + /** @test */ + public function doneShouldThrowExceptionProvidedAsRejectionValueForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $adapter->reject(new \Exception('UnhandledRejectionException')); + $this->assertNull($adapter->promise()->done()); + } + + /** @test */ + public function doneShouldThrowWithDeepNestingPromiseChainsForRejectedPromise() + { + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $exception = new \Exception('UnhandledRejectionException'); + + $d = new Deferred(); + $d->resolve(); + + $result = \React\Promise\resolve(\React\Promise\resolve($d->promise()->then(function () use ($exception) { + $d = new Deferred(); + $d->resolve(); + + return \React\Promise\resolve($d->promise()->then(function () {}))->then( + function () use ($exception) { + throw $exception; + } + ); + }))); + + $result->done(); + } + + /** @test */ + public function doneShouldRecoverWhenRejectionHandlerCatchesExceptionForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->reject(new \Exception('UnhandledRejectionException')); + $this->assertNull($adapter->promise()->done(null, function (\Exception $e) { + + })); + } + + /** @test */ + public function otherwiseShouldInvokeRejectionHandlerForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->reject(1); + $adapter->promise()->otherwise($mock); + } + + /** @test */ + public function otherwiseShouldInvokeNonTypeHintedRejectionHandlerIfReasonIsAnExceptionForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->reject($exception); + $adapter->promise() + ->otherwise(function ($reason) use ($mock) { + $mock($reason); + }); + } + + /** @test */ + public function otherwiseShouldInvokeRejectionHandlerIfReasonMatchesTypehintForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \InvalidArgumentException(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->reject($exception); + $adapter->promise() + ->otherwise(function (\InvalidArgumentException $reason) use ($mock) { + $mock($reason); + }); + } + + /** @test */ + public function otherwiseShouldNotInvokeRejectionHandlerIfReaonsDoesNotMatchTypehintForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->expectCallableNever(); + + $adapter->reject($exception); + $adapter->promise() + ->otherwise(function (\InvalidArgumentException $reason) use ($mock) { + $mock($reason); + }); + } + + /** @test */ + public function alwaysShouldNotSuppressRejectionForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->reject($exception); + $adapter->promise() + ->always(function () {}) + ->then(null, $mock); + } + + /** @test */ + public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsANonPromiseForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->reject($exception); + $adapter->promise() + ->always(function () { + return 1; + }) + ->then(null, $mock); + } + + /** @test */ + public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsAPromiseForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->reject($exception); + $adapter->promise() + ->always(function () { + return \React\Promise\resolve(1); + }) + ->then(null, $mock); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerThrowsForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception1 = new \Exception(); + $exception2 = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception2)); + + $adapter->reject($exception1); + $adapter->promise() + ->always(function () use ($exception2) { + throw $exception2; + }) + ->then(null, $mock); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerRejectsForRejectedPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception1 = new \Exception(); + $exception2 = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception2)); + + $adapter->reject($exception1); + $adapter->promise() + ->always(function () use ($exception2) { + return \React\Promise\reject($exception2); + }) + ->then(null, $mock); + } + /** @test */ public function cancelShouldReturnNullForRejectedPromise() { diff --git a/core/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php index c56028471bbc..e363b6d91c41 100644 --- a/core/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php +++ b/core/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php @@ -46,4 +46,41 @@ public function cancelShouldHaveNoEffectForSettledPromise() $adapter->promise()->cancel(); } + + /** @test */ + public function doneShouldReturnNullForSettledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->settle(); + $this->assertNull($adapter->promise()->done(null, function () {})); + } + + /** @test */ + public function doneShouldReturnAllowNullForSettledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->settle(); + $this->assertNull($adapter->promise()->done(null, function () {}, null)); + } + + /** @test */ + public function progressShouldNotInvokeProgressHandlerForSettledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->settle(); + $adapter->promise()->progress($this->expectCallableNever()); + $adapter->notify(); + } + + /** @test */ + public function alwaysShouldReturnAPromiseForSettledPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $adapter->settle(); + $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->always(function () {})); + } } diff --git a/core/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php index 66e5e734ef7c..7d6f65fd0ae3 100644 --- a/core/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php +++ b/core/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php @@ -3,6 +3,7 @@ namespace React\Promise\PromiseTest; use React\Promise; +use React\Promise\Deferred; trait RejectTestTrait { @@ -105,4 +106,258 @@ public function rejectShouldMakePromiseImmutable() $adapter->reject(1); $adapter->reject(2); } + + /** @test */ + public function notifyShouldInvokeOtherwiseHandler() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $adapter->promise() + ->otherwise($mock); + + $adapter->reject(1); + } + + /** @test */ + public function doneShouldInvokeRejectionHandler() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $this->assertNull($adapter->promise()->done(null, $mock)); + $adapter->reject(1); + } + + /** @test */ + public function doneShouldThrowExceptionThrownByRejectionHandler() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done(null, function () { + throw new \Exception('UnhandledRejectionException'); + })); + $adapter->reject(1); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenRejectedWithNonException() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done()); + $adapter->reject(1); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRejects() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done(null, function () { + return \React\Promise\reject(); + })); + $adapter->reject(1); + } + + /** @test */ + public function doneShouldThrowRejectionExceptionWhenRejectionHandlerRejectsWithException() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done(null, function () { + return \React\Promise\reject(new \Exception('UnhandledRejectionException')); + })); + $adapter->reject(1); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRetunsPendingPromiseWhichRejectsLater() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $d = new Deferred(); + $promise = $d->promise(); + + $this->assertNull($adapter->promise()->done(null, function () use ($promise) { + return $promise; + })); + $adapter->reject(1); + $d->reject(1); + } + + /** @test */ + public function doneShouldThrowExceptionProvidedAsRejectionValue() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done()); + $adapter->reject(new \Exception('UnhandledRejectionException')); + } + + /** @test */ + public function doneShouldThrowWithDeepNestingPromiseChains() + { + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $exception = new \Exception('UnhandledRejectionException'); + + $d = new Deferred(); + + $result = \React\Promise\resolve(\React\Promise\resolve($d->promise()->then(function () use ($exception) { + $d = new Deferred(); + $d->resolve(); + + return \React\Promise\resolve($d->promise()->then(function () {}))->then( + function () use ($exception) { + throw $exception; + } + ); + }))); + + $result->done(); + + $d->resolve(); + } + + /** @test */ + public function doneShouldRecoverWhenRejectionHandlerCatchesException() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->assertNull($adapter->promise()->done(null, function (\Exception $e) { + + })); + $adapter->reject(new \Exception('UnhandledRejectionException')); + } + + /** @test */ + public function alwaysShouldNotSuppressRejection() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () {}) + ->then(null, $mock); + + $adapter->reject($exception); + } + + /** @test */ + public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsANonPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () { + return 1; + }) + ->then(null, $mock); + + $adapter->reject($exception); + } + + /** @test */ + public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsAPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () { + return \React\Promise\resolve(1); + }) + ->then(null, $mock); + + $adapter->reject($exception); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerThrowsForRejection() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () use ($exception) { + throw $exception; + }) + ->then(null, $mock); + + $adapter->reject($exception); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerRejectsForRejection() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () use ($exception) { + return \React\Promise\reject($exception); + }) + ->then(null, $mock); + + $adapter->reject($exception); + } } diff --git a/core/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php index 4ee201916142..b05f7fe84bbf 100644 --- a/core/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php +++ b/core/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php @@ -106,4 +106,153 @@ public function resolveShouldMakePromiseImmutable() $adapter->resolve(1); $adapter->resolve(2); } + + /** @test */ + public function doneShouldInvokeFulfillmentHandler() + { + $adapter = $this->getPromiseTestAdapter(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $this->assertNull($adapter->promise()->done($mock)); + $adapter->resolve(1); + } + + /** @test */ + public function doneShouldThrowExceptionThrownFulfillmentHandler() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('\Exception', 'UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done(function () { + throw new \Exception('UnhandledRejectionException'); + })); + $adapter->resolve(1); + } + + /** @test */ + public function doneShouldThrowUnhandledRejectionExceptionWhenFulfillmentHandlerRejects() + { + $adapter = $this->getPromiseTestAdapter(); + + $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); + + $this->assertNull($adapter->promise()->done(function () { + return \React\Promise\reject(); + })); + $adapter->resolve(1); + } + + /** @test */ + public function alwaysShouldNotSuppressValue() + { + $adapter = $this->getPromiseTestAdapter(); + + $value = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($value)); + + $adapter->promise() + ->always(function () {}) + ->then($mock); + + $adapter->resolve($value); + } + + /** @test */ + public function alwaysShouldNotSuppressValueWhenHandlerReturnsANonPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $value = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($value)); + + $adapter->promise() + ->always(function () { + return 1; + }) + ->then($mock); + + $adapter->resolve($value); + } + + /** @test */ + public function alwaysShouldNotSuppressValueWhenHandlerReturnsAPromise() + { + $adapter = $this->getPromiseTestAdapter(); + + $value = new \stdClass(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($value)); + + $adapter->promise() + ->always(function () { + return \React\Promise\resolve(1); + }) + ->then($mock); + + $adapter->resolve($value); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerThrowsForFulfillment() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () use ($exception) { + throw $exception; + }) + ->then(null, $mock); + + $adapter->resolve(1); + } + + /** @test */ + public function alwaysShouldRejectWhenHandlerRejectsForFulfillment() + { + $adapter = $this->getPromiseTestAdapter(); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $adapter->promise() + ->always(function () use ($exception) { + return \React\Promise\reject($exception); + }) + ->then(null, $mock); + + $adapter->resolve(1); + } } diff --git a/core/vendor/react/promise/tests/RejectedPromiseTest.php b/core/vendor/react/promise/tests/RejectedPromiseTest.php index 040dd2e2cc2e..c886b0095420 100644 --- a/core/vendor/react/promise/tests/RejectedPromiseTest.php +++ b/core/vendor/react/promise/tests/RejectedPromiseTest.php @@ -29,8 +29,8 @@ public function getPromiseTestAdapter(callable $canceller = null) $promise = new RejectedPromise($reason); } }, - 'progress' => function () { - throw new \LogicException('You cannot call progress() for React\Promise\RejectedPromise'); + 'notify' => function () { + // no-op }, 'settle' => function ($reason = null) use (&$promise) { if (!$promise) { -- GitLab