From 90c4b5d9fa77319b43c39ce70921460d69459182 Mon Sep 17 00:00:00 2001 From: Juuso Leinonen Date: Thu, 22 Dec 2016 14:43:11 +0200 Subject: [PATCH 1/3] Changed ReactPromiseAdapter::all to preserve the array key order and added tests --- phpunit.xml.dist | 6 + .../Promise/Adapter/ReactPromiseAdapter.php | 11 +- .../Promise/ReactPromiseAdapterTest.php | 161 ++++++++++++++++++ 3 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 tests/Executor/Promise/ReactPromiseAdapterTest.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f617b48..9927677 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -17,6 +17,12 @@ + + + ReactPromise + + + ./src diff --git a/src/Executor/Promise/Adapter/ReactPromiseAdapter.php b/src/Executor/Promise/Adapter/ReactPromiseAdapter.php index 873d5b8..fe30255 100644 --- a/src/Executor/Promise/Adapter/ReactPromiseAdapter.php +++ b/src/Executor/Promise/Adapter/ReactPromiseAdapter.php @@ -71,7 +71,16 @@ class ReactPromiseAdapter implements PromiseAdapter $promisesOrValues = Utils::map($promisesOrValues, function ($item) { return $item instanceof Promise ? $item->adoptedPromise : $item; }); - $promise = \React\Promise\all($promisesOrValues); + + $promise = \React\Promise\all($promisesOrValues)->then(function($values) use ($promisesOrValues) { + $orderedResults = []; + + foreach ($promisesOrValues as $key => $value) { + $orderedResults[$key] = $values[$key]; + } + + return $orderedResults; + }); return new Promise($promise, $this); } } diff --git a/tests/Executor/Promise/ReactPromiseAdapterTest.php b/tests/Executor/Promise/ReactPromiseAdapterTest.php new file mode 100644 index 0000000..e0ed8bb --- /dev/null +++ b/tests/Executor/Promise/ReactPromiseAdapterTest.php @@ -0,0 +1,161 @@ +markTestSkipped('react/promise package must be installed to run ' . self::class); + } + } + + public function testIsThenableReturnsTrueWhenAReactPromiseIsGiven() + { + $reactAdapter = new ReactPromiseAdapter(); + + $this->assertSame(true, $reactAdapter->isThenable(new ReactPromise(function() {}))); + $this->assertSame(true, $reactAdapter->isThenable(new FulfilledPromise())); + $this->assertSame(true, $reactAdapter->isThenable(new RejectedPromise())); + $this->assertSame(true, $reactAdapter->isThenable(new LazyPromise(function() {}))); + $this->assertSame(false, $reactAdapter->isThenable(false)); + $this->assertSame(false, $reactAdapter->isThenable(true)); + $this->assertSame(false, $reactAdapter->isThenable(1)); + $this->assertSame(false, $reactAdapter->isThenable(0)); + $this->assertSame(false, $reactAdapter->isThenable('test')); + $this->assertSame(false, $reactAdapter->isThenable('')); + $this->assertSame(false, $reactAdapter->isThenable([])); + $this->assertSame(false, $reactAdapter->isThenable(new \stdClass())); + } + + public function testConvertsReactPromisesToGraphQlOnes() + { + $reactAdapter = new ReactPromiseAdapter(); + $reactPromise = new FulfilledPromise(1); + + $promise = $reactAdapter->convertThenable($reactPromise); + + $this->assertInstanceOf(Promise::class, $promise); + $this->assertInstanceOf(FulfilledPromise::class, $promise->adoptedPromise); + } + + public function testThen() + { + $reactAdapter = new ReactPromiseAdapter(); + $reactPromise = new FulfilledPromise(1); + $promise = $reactAdapter->convertThenable($reactPromise); + + $result = null; + + $resultPromise = $reactAdapter->then($promise, function ($value) use (&$result) { + $result = $value; + }); + + $this->assertSame(1, $result); + $this->assertInstanceOf(Promise::class, $resultPromise); + $this->assertInstanceOf(FulfilledPromise::class, $resultPromise->adoptedPromise); + } + + public function testCreate() + { + $reactAdapter = new ReactPromiseAdapter(); + $resolvedPromise = $reactAdapter->create(function ($resolve) { + $resolve(1); + }); + + $this->assertInstanceOf(Promise::class, $resolvedPromise); + $this->assertInstanceOf(ReactPromise::class, $resolvedPromise->adoptedPromise); + + $result = null; + + $resolvedPromise->then(function ($value) use (&$result) { + $result = $value; + }); + + $this->assertSame(1, $result); + } + + public function testCreateFulfilled() + { + $reactAdapter = new ReactPromiseAdapter(); + $fulfilledPromise = $reactAdapter->createFulfilled(1); + + $this->assertInstanceOf(Promise::class, $fulfilledPromise); + $this->assertInstanceOf(FulfilledPromise::class, $fulfilledPromise->adoptedPromise); + + $result = null; + + $fulfilledPromise->then(function ($value) use (&$result) { + $result = $value; + }); + + $this->assertSame(1, $result); + } + + public function testCreateRejected() + { + $reactAdapter = new ReactPromiseAdapter(); + $rejectedPromise = $reactAdapter->createRejected(new \Exception('I am a bad promise')); + + $this->assertInstanceOf(Promise::class, $rejectedPromise); + $this->assertInstanceOf(RejectedPromise::class, $rejectedPromise->adoptedPromise); + + $exception = null; + + $rejectedPromise->then(null, function ($error) use (&$exception) { + $exception = $error; + }); + + $this->assertInstanceOf(\Exception::class, $exception); + $this->assertEquals('I am a bad promise', $exception->getMessage()); + } + + public function testAll() + { + $reactAdapter = new ReactPromiseAdapter(); + $promises = [new FulfilledPromise(1), new FulfilledPromise(2), new FulfilledPromise(3)]; + + $allPromise = $reactAdapter->all($promises); + + $this->assertInstanceOf(Promise::class, $allPromise); + $this->assertInstanceOf(FulfilledPromise::class, $allPromise->adoptedPromise); + + $result = null; + + $allPromise->then(function ($values) use (&$result) { + $result = $values; + }); + + $this->assertSame([1, 2, 3], $result); + } + + public function testAllShouldPreserveTheOrderOfTheArrayWhenResolvingAsyncPromises() + { + $reactAdapter = new ReactPromiseAdapter(); + $deferred = new Deferred(); + $promises = [new FulfilledPromise(1), $deferred->promise(), new FulfilledPromise(3)]; + $result = null; + + $reactAdapter->all($promises)->then(function ($values) use (&$result) { + $result = $values; + }); + + // Resolve the async promise + $deferred->resolve(2); + $this->assertSame([1, 2, 3], $result); + } +} From e3a864f0711f4e171539e71d4e970235af7e7ccc Mon Sep 17 00:00:00 2001 From: Juuso Leinonen Date: Thu, 22 Dec 2016 15:24:47 +0200 Subject: [PATCH 2/3] Make travis run also the ReactPromiseAdapter tests --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c80a898..4a7fb5b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,8 +22,9 @@ before_install: install: - composer install --dev --prefer-dist + - composer require react/promise:2.* -script: if [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then phpunit --coverage-clover build/logs/clover.xml; else phpunit; fi +script: if [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then phpunit --coverage-clover build/logs/clover.xml --group default,ReactPromise; else phpunit --group default,ReactPromise; fi after_success: - if [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then composer require "satooshi/php-coveralls:^1.0" && travis_retry php bin/coveralls -v; fi From 8626e0b45d1305b43d9f9e297a608c6269e2b3c6 Mon Sep 17 00:00:00 2001 From: Juuso Leinonen Date: Thu, 22 Dec 2016 15:31:56 +0200 Subject: [PATCH 3/3] Make tests run on php 5.4 --- .../Promise/ReactPromiseAdapterTest.php | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/Executor/Promise/ReactPromiseAdapterTest.php b/tests/Executor/Promise/ReactPromiseAdapterTest.php index e0ed8bb..436889f 100644 --- a/tests/Executor/Promise/ReactPromiseAdapterTest.php +++ b/tests/Executor/Promise/ReactPromiseAdapterTest.php @@ -19,8 +19,8 @@ class ReactPromiseAdapterTest extends \PHPUnit_Framework_TestCase { public function setUp() { - if(! class_exists(ReactPromise::class)) { - $this->markTestSkipped('react/promise package must be installed to run ' . self::class); + if(! class_exists('React\Promise\Promise')) { + $this->markTestSkipped('react/promise package must be installed to run GraphQL\Tests\Executor\Promise\ReactPromiseAdapterTest'); } } @@ -49,8 +49,8 @@ class ReactPromiseAdapterTest extends \PHPUnit_Framework_TestCase $promise = $reactAdapter->convertThenable($reactPromise); - $this->assertInstanceOf(Promise::class, $promise); - $this->assertInstanceOf(FulfilledPromise::class, $promise->adoptedPromise); + $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $promise); + $this->assertInstanceOf('React\Promise\FulfilledPromise', $promise->adoptedPromise); } public function testThen() @@ -66,8 +66,8 @@ class ReactPromiseAdapterTest extends \PHPUnit_Framework_TestCase }); $this->assertSame(1, $result); - $this->assertInstanceOf(Promise::class, $resultPromise); - $this->assertInstanceOf(FulfilledPromise::class, $resultPromise->adoptedPromise); + $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $resultPromise); + $this->assertInstanceOf('React\Promise\FulfilledPromise', $resultPromise->adoptedPromise); } public function testCreate() @@ -77,8 +77,8 @@ class ReactPromiseAdapterTest extends \PHPUnit_Framework_TestCase $resolve(1); }); - $this->assertInstanceOf(Promise::class, $resolvedPromise); - $this->assertInstanceOf(ReactPromise::class, $resolvedPromise->adoptedPromise); + $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $resolvedPromise); + $this->assertInstanceOf('React\Promise\Promise', $resolvedPromise->adoptedPromise); $result = null; @@ -94,8 +94,8 @@ class ReactPromiseAdapterTest extends \PHPUnit_Framework_TestCase $reactAdapter = new ReactPromiseAdapter(); $fulfilledPromise = $reactAdapter->createFulfilled(1); - $this->assertInstanceOf(Promise::class, $fulfilledPromise); - $this->assertInstanceOf(FulfilledPromise::class, $fulfilledPromise->adoptedPromise); + $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $fulfilledPromise); + $this->assertInstanceOf('React\Promise\FulfilledPromise', $fulfilledPromise->adoptedPromise); $result = null; @@ -111,8 +111,8 @@ class ReactPromiseAdapterTest extends \PHPUnit_Framework_TestCase $reactAdapter = new ReactPromiseAdapter(); $rejectedPromise = $reactAdapter->createRejected(new \Exception('I am a bad promise')); - $this->assertInstanceOf(Promise::class, $rejectedPromise); - $this->assertInstanceOf(RejectedPromise::class, $rejectedPromise->adoptedPromise); + $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $rejectedPromise); + $this->assertInstanceOf('React\Promise\RejectedPromise', $rejectedPromise->adoptedPromise); $exception = null; @@ -120,7 +120,7 @@ class ReactPromiseAdapterTest extends \PHPUnit_Framework_TestCase $exception = $error; }); - $this->assertInstanceOf(\Exception::class, $exception); + $this->assertInstanceOf('\Exception', $exception); $this->assertEquals('I am a bad promise', $exception->getMessage()); } @@ -131,8 +131,8 @@ class ReactPromiseAdapterTest extends \PHPUnit_Framework_TestCase $allPromise = $reactAdapter->all($promises); - $this->assertInstanceOf(Promise::class, $allPromise); - $this->assertInstanceOf(FulfilledPromise::class, $allPromise->adoptedPromise); + $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $allPromise); + $this->assertInstanceOf('React\Promise\FulfilledPromise', $allPromise->adoptedPromise); $result = null;