Browse Source

Merge pull request #12184 from cakephp/issue-12150

Fix URL arrays not working with IntegrationTestCase and RoutingMiddleware
Mark Story 7 years ago
parent
commit
7505bb8111

+ 4 - 2
src/TestSuite/IntegrationTestCase.php

@@ -488,6 +488,8 @@ abstract class IntegrationTestCase extends TestCase
     protected function _sendRequest($url, $method, $data = [])
     {
         $dispatcher = $this->_makeDispatcher();
+        $url = $dispatcher->resolveUrl($url);
+
         try {
             $request = $this->_buildRequest($url, $method, $data);
             $response = $dispatcher->execute($request);
@@ -661,14 +663,14 @@ abstract class IntegrationTestCase extends TestCase
     /**
      * Creates a valid request url and parameter array more like Request::_url()
      *
-     * @param string|array $url The URL
+     * @param string $url The URL
      * @return array Qualified URL and the query parameters
      */
     protected function _url($url)
     {
         // re-create URL in ServerRequest's context so
         // query strings are encoded as expected
-        $request = new ServerRequest(['url' => Router::url($url)]);
+        $request = new ServerRequest(['url' => $url]);
         $url = $request->getRequestTarget();
 
         $query = '';

+ 12 - 0
src/TestSuite/LegacyRequestDispatcher.php

@@ -15,6 +15,7 @@ namespace Cake\TestSuite;
 
 use Cake\Http\ServerRequest;
 use Cake\Routing\DispatcherFactory;
+use Cake\Routing\Router;
 use Cake\TestSuite\Stub\Response;
 
 /**
@@ -42,6 +43,17 @@ class LegacyRequestDispatcher
     }
 
     /**
+     * Resolve the user provided URL into the actual request URL.
+     *
+     * @param array|string $url The URL array/string to resolve.
+     * @return string
+     */
+    public function resolveUrl($url)
+    {
+        return Router::url($url);
+    }
+
+    /**
      * Run a request and get the response.
      *
      * @param array $request The request context to execute.

+ 63 - 11
src/TestSuite/MiddlewareDispatcher.php

@@ -14,9 +14,12 @@
 namespace Cake\TestSuite;
 
 use Cake\Core\Configure;
+use Cake\Core\HttpApplicationInterface;
+use Cake\Core\PluginApplicationInterface;
 use Cake\Event\EventManager;
 use Cake\Http\Server;
 use Cake\Http\ServerRequestFactory;
+use Cake\Routing\Router;
 use LogicException;
 use ReflectionClass;
 use ReflectionException;
@@ -52,6 +55,13 @@ class MiddlewareDispatcher
     protected $_constructorArgs;
 
     /**
+     * The application that is being dispatched.
+     *
+     * @var \Cake\Core\HttpApplicationInterface
+     */
+    protected $app;
+
+    /**
      * Constructor
      *
      * @param \Cake\TestSuite\IntegrationTestCase $test The test case to run.
@@ -64,6 +74,58 @@ class MiddlewareDispatcher
         $this->_test = $test;
         $this->_class = $class ?: Configure::read('App.namespace') . '\Application';
         $this->_constructorArgs = $constructorArgs ?: [CONFIG];
+
+        try {
+            $reflect = new ReflectionClass($this->_class);
+            $this->app = $reflect->newInstanceArgs($this->_constructorArgs);
+        } catch (ReflectionException $e) {
+            throw new LogicException(sprintf('Cannot load "%s" for use in integration testing.', $this->_class));
+        }
+    }
+
+    /**
+     * Resolve the provided URL into a string.
+     *
+     * @param array|string $url The URL array/string to resolve.
+     * @return string
+     */
+    public function resolveUrl($url)
+    {
+        // If we need to resolve a Route URL but there are no routes, load routes.
+        if (is_array($url) && count(Router::getRouteCollection()->routes()) === 0) {
+            return $this->resolveRoute($url);
+        }
+
+        return Router::url($url);
+    }
+
+    /**
+     * Convert a URL array into a string URL via routing.
+     *
+     * @param array $url The url to resolve
+     * @return string
+     */
+    protected function resolveRoute(array $url)
+    {
+        // Simulate application bootstrap and route loading.
+        // We need both to ensure plugins are loaded.
+        $this->app->bootstrap();
+        if ($this->app instanceof PluginApplicationInterface) {
+            $this->app->pluginBootstrap();
+        }
+        $builder = Router::createRouteBuilder('/');
+
+        if ($this->app instanceof HttpApplicationInterface) {
+            $this->app->routes($builder);
+        }
+        if ($this->app instanceof PluginApplicationInterface) {
+            $this->app->pluginRoutes($builder);
+        }
+
+        $out = Router::url($url);
+        Router::reload();
+
+        return $out;
     }
 
     /**
@@ -74,16 +136,6 @@ class MiddlewareDispatcher
      */
     public function execute($request)
     {
-        try {
-            $reflect = new ReflectionClass($this->_class);
-            $app = $reflect->newInstanceArgs($this->_constructorArgs);
-        } catch (ReflectionException $e) {
-            throw new LogicException(sprintf(
-                'Cannot load "%s" for use in integration testing.',
-                $this->_class
-            ));
-        }
-
         // Spy on the controller using the initialize hook instead
         // of the dispatcher hooks as those will be going away one day.
         EventManager::instance()->on(
@@ -91,7 +143,7 @@ class MiddlewareDispatcher
             [$this->_test, 'controllerSpy']
         );
 
-        $server = new Server($app);
+        $server = new Server($this->app);
         $psrRequest = $this->_createRequest($request);
 
         return $server->run($psrRequest);

+ 14 - 0
tests/TestCase/TestSuite/IntegrationTestCaseTest.php

@@ -512,6 +512,20 @@ class IntegrationTestCaseTest extends IntegrationTestCase
     }
 
     /**
+     * Test array URLs with an empty router.
+     *
+     * @return void
+     */
+    public function testArrayUrlsEmptyRouter()
+    {
+        Router::reload();
+        $this->assertFalse(Router::$initialized);
+
+        $this->post(['controller' => 'Posts', 'action' => 'index']);
+        $this->assertEquals('value', $this->viewVariable('test'));
+    }
+
+    /**
      * Test flash and cookie assertions
      *
      * @return void