From 90c4b5d9fa77319b43c39ce70921460d69459182 Mon Sep 17 00:00:00 2001 From: Juuso Leinonen Date: Thu, 22 Dec 2016 14:43:11 +0200 Subject: [PATCH] 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); + } +}