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
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..436889f
--- /dev/null
+++ b/tests/Executor/Promise/ReactPromiseAdapterTest.php
@@ -0,0 +1,161 @@
+markTestSkipped('react/promise package must be installed to run GraphQL\Tests\Executor\Promise\ReactPromiseAdapterTest');
+ }
+ }
+
+ 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('GraphQL\Executor\Promise\Promise', $promise);
+ $this->assertInstanceOf('React\Promise\FulfilledPromise', $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('GraphQL\Executor\Promise\Promise', $resultPromise);
+ $this->assertInstanceOf('React\Promise\FulfilledPromise', $resultPromise->adoptedPromise);
+ }
+
+ public function testCreate()
+ {
+ $reactAdapter = new ReactPromiseAdapter();
+ $resolvedPromise = $reactAdapter->create(function ($resolve) {
+ $resolve(1);
+ });
+
+ $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $resolvedPromise);
+ $this->assertInstanceOf('React\Promise\Promise', $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('GraphQL\Executor\Promise\Promise', $fulfilledPromise);
+ $this->assertInstanceOf('React\Promise\FulfilledPromise', $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('GraphQL\Executor\Promise\Promise', $rejectedPromise);
+ $this->assertInstanceOf('React\Promise\RejectedPromise', $rejectedPromise->adoptedPromise);
+
+ $exception = null;
+
+ $rejectedPromise->then(null, function ($error) use (&$exception) {
+ $exception = $error;
+ });
+
+ $this->assertInstanceOf('\Exception', $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('GraphQL\Executor\Promise\Promise', $allPromise);
+ $this->assertInstanceOf('React\Promise\FulfilledPromise', $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);
+ }
+}