RouterTest.php 12.7 KB
Newer Older
1 2 3 4
<?php

namespace Drupal\system\Tests\Routing;

5
use Drupal\Core\Cache\Cache;
6
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
7
use Drupal\Core\Language\LanguageInterface;
8
use Drupal\simpletest\WebTestBase;
9
use Symfony\Component\Routing\Exception\RouteNotFoundException;
10
use Drupal\Core\Url;
11 12 13

/**
 * Functional class for the full integrated routing system.
14 15
 *
 * @group Routing
16 17 18 19 20 21 22 23
 */
class RouterTest extends WebTestBase {

  /**
   * Modules to enable.
   *
   * @var array
   */
24
  public static $modules = array('router_test');
25

26
  /**
27
   * Confirms that our FinishResponseSubscriber logic works properly.
28
   */
29
  public function testFinishResponseSubscriber() {
30
    $renderer_required_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'];
31
    $expected_cache_contexts = Cache::mergeContexts($renderer_required_cache_contexts, ['url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT]);
32

33
    // Confirm that the router can get to a controller.
34 35
    $this->drupalGet('router_test/test1');
    $this->assertRaw('test1', 'The correct string was returned because the route was successful.');
36
    // Check expected headers from FinishResponseSubscriber.
37
    $headers = $this->drupalGetHeaders();
38
    $this->assertEqual($headers['x-ua-compatible'], 'IE=edge');
39 40
    $this->assertEqual($headers['content-language'], 'en');
    $this->assertEqual($headers['x-content-type-options'], 'nosniff');
41
    $this->assertEqual($headers['x-frame-options'], 'SAMEORIGIN');
42

43 44
    $this->drupalGet('router_test/test2');
    $this->assertRaw('test2', 'The correct string was returned because the route was successful.');
45 46
    // Check expected headers from FinishResponseSubscriber.
    $headers = $this->drupalGetHeaders();
47
    $this->assertEqual($headers['x-drupal-cache-contexts'], implode(' ', $expected_cache_contexts));
48
    $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous http_response rendered');
49 50
    // Confirm that the page wrapping is being added, so we're not getting a
    // raw body returned.
51
    $this->assertRaw('</html>', 'Page markup was found.');
52 53 54
    // In some instances, the subrequest handling may get confused and render
    // a page inception style.  This test verifies that is not happening.
    $this->assertNoPattern('#</body>.*</body>#s', 'There was no double-page effect from a misrendered subrequest.');
55 56 57 58 59 60 61 62


    // Confirm that route-level access check's cacheability is applied to the
    // X-Drupal-Cache-Contexts and X-Drupal-Cache-Tags headers.
    // 1. controller result: render array, globally cacheable route access.
    $this->drupalGet('router_test/test18');
    $headers = $this->drupalGetHeaders();
    $this->assertEqual($headers['x-drupal-cache-contexts'], implode(' ', Cache::mergeContexts($renderer_required_cache_contexts, ['url'])));
63
    $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous foo http_response rendered');
64 65 66 67
    // 2. controller result: render array, per-role cacheable route access.
    $this->drupalGet('router_test/test19');
    $headers = $this->drupalGetHeaders();
    $this->assertEqual($headers['x-drupal-cache-contexts'], implode(' ', Cache::mergeContexts($renderer_required_cache_contexts, ['url', 'user.roles'])));
68
    $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous foo http_response rendered');
69 70 71
    // 3. controller result: Response object, globally cacheable route access.
    $this->drupalGet('router_test/test1');
    $headers = $this->drupalGetHeaders();
72 73
    $this->assertFalse(isset($headers['x-drupal-cache-contexts']));
    $this->assertFalse(isset($headers['x-drupal-cache-tags']));
74 75 76
    // 4. controller result: Response object, per-role cacheable route access.
    $this->drupalGet('router_test/test20');
    $headers = $this->drupalGetHeaders();
77 78 79 80 81 82
    $this->assertFalse(isset($headers['x-drupal-cache-contexts']));
    $this->assertFalse(isset($headers['x-drupal-cache-tags']));
    // 5. controller result: CacheableResponse object, globally cacheable route access.
    $this->drupalGet('router_test/test21');
    $headers = $this->drupalGetHeaders();
    $this->assertEqual($headers['x-drupal-cache-contexts'], '');
83
    $this->assertEqual($headers['x-drupal-cache-tags'], 'http_response');
84 85 86
    // 6. controller result: CacheableResponse object, per-role cacheable route access.
    $this->drupalGet('router_test/test22');
    $headers = $this->drupalGetHeaders();
87
    $this->assertEqual($headers['x-drupal-cache-contexts'], 'user.roles');
88
    $this->assertEqual($headers['x-drupal-cache-tags'], 'http_response');
89 90 91 92 93 94 95 96 97 98 99 100

    // Finally, verify that the X-Drupal-Cache-Contexts and X-Drupal-Cache-Tags
    // headers are not sent when their container parameter is set to FALSE.
    $this->drupalGet('router_test/test18');
    $headers = $this->drupalGetHeaders();
    $this->assertTrue(isset($headers['x-drupal-cache-contexts']));
    $this->assertTrue(isset($headers['x-drupal-cache-tags']));
    $this->setHttpResponseDebugCacheabilityHeaders(FALSE);
    $this->drupalGet('router_test/test18');
    $headers = $this->drupalGetHeaders();
    $this->assertFalse(isset($headers['x-drupal-cache-contexts']));
    $this->assertFalse(isset($headers['x-drupal-cache-tags']));
101 102
  }

103 104 105
  /**
   * Confirms that placeholders in paths work correctly.
   */
106
  public function testControllerPlaceholders() {
107
    // Test with 0 and a random value.
108
    $values = array("0", $this->randomMachineName());
109 110 111 112 113
    foreach ($values as $value) {
      $this->drupalGet('router_test/test3/' . $value);
      $this->assertResponse(200);
      $this->assertRaw($value, 'The correct string was returned because the route was successful.');
    }
114 115 116

    // Confirm that the page wrapping is being added, so we're not getting a
    // raw body returned.
117
    $this->assertRaw('</html>', 'Page markup was found.');
118 119 120 121

    // In some instances, the subrequest handling may get confused and render
    // a page inception style.  This test verifies that is not happening.
    $this->assertNoPattern('#</body>.*</body>#s', 'There was no double-page effect from a misrendered subrequest.');
122 123
  }

124 125 126 127 128
  /**
   * Confirms that default placeholders in paths work correctly.
   */
  public function testControllerPlaceholdersDefaultValues() {
    $this->drupalGet('router_test/test4');
129
    $this->assertResponse(200);
130
    $this->assertRaw('narf', 'The correct string was returned because the route was successful.');
131 132

    // Confirm that the page wrapping is being added, so we're not getting a
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
    // raw body returned.
    $this->assertRaw('</html>', 'Page markup was found.');

    // In some instances, the subrequest handling may get confused and render
    // a page inception style.  This test verifies that is not happening.
    $this->assertNoPattern('#</body>.*</body>#s', 'There was no double-page effect from a misrendered subrequest.');
  }

  /**
   * Confirms that default placeholders in paths work correctly.
   */
  public function testControllerPlaceholdersDefaultValuesProvided() {
    $this->drupalGet('router_test/test4/barf');
    $this->assertResponse(200);
    $this->assertRaw('barf', 'The correct string was returned because the route was successful.');

    // Confirm that the page wrapping is being added, so we're not getting a
150
    // raw body returned.
151
    $this->assertRaw('</html>', 'Page markup was found.');
152 153 154 155

    // In some instances, the subrequest handling may get confused and render
    // a page inception style.  This test verifies that is not happening.
    $this->assertNoPattern('#</body>.*</body>#s', 'There was no double-page effect from a misrendered subrequest.');
156 157
  }

158 159 160 161 162 163 164 165 166 167 168
  /**
   * Checks that dynamically defined and altered routes work correctly.
   *
   * @see \Drupal\router_test\RouteSubscriber
   */
  public function testDynamicRoutes() {
    // Test the altered route.
    $this->drupalGet('router_test/test6');
    $this->assertResponse(200);
    $this->assertRaw('test5', 'The correct string was returned because the route was successful.');
  }
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

  /**
   * Checks that a request with text/html response gets rendered as a page.
   */
  public function testControllerResolutionPage() {
    $this->drupalGet('/router_test/test10');

    $this->assertRaw('abcde', 'Correct body was found.');

    // Confirm that the page wrapping is being added, so we're not getting a
    // raw body returned.
    $this->assertRaw('</html>', 'Page markup was found.');

    // In some instances, the subrequest handling may get confused and render
    // a page inception style. This test verifies that is not happening.
    $this->assertNoPattern('#</body>.*</body>#s', 'There was no double-page effect from a misrendered subrequest.');
  }

187 188 189 190
  /**
   * Checks the generate method on the url generator using the front router.
   */
  public function testUrlGeneratorFront() {
191 192 193 194
    $front_url = Url::fromRoute('<front>', [], ['absolute' => TRUE]);
    // Compare to the site base URL.
    $base_url = Url::fromUri('base:/', ['absolute' => TRUE]);
    $this->assertIdentical($base_url->toString(), $front_url->toString());
195 196
  }

197 198 199 200 201 202
  /**
   * Tests that a page trying to match a path will succeed.
   */
  public function testRouterMatching() {
    $this->drupalGet('router_test/test14/1');
    $this->assertResponse(200);
203
    $this->assertText('User route "entity.user.canonical" was matched.');
204 205 206 207 208

    // Try to match a route for a non-existent user.
    $this->drupalGet('router_test/test14/2');
    $this->assertResponse(200);
    $this->assertText('Route not matched.');
209 210 211 212 213 214 215 216 217

    // Check that very long paths don't cause an error.
    $path = 'router_test/test1';
    $suffix = '/d/r/u/p/a/l';
    for ($i = 0; $i < 10; $i++) {
      $path .= $suffix;
      $this->drupalGet($path);
      $this->assertResponse(404);
    }
218 219
  }

220 221 222 223 224 225 226 227 228
  /**
   * Tests that a PSR-7 response works.
   */
  public function testRouterResponsePsr7() {
    $this->drupalGet('/router_test/test23');
    $this->assertResponse(200);
    $this->assertText('test23');
  }

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
  /**
   * Tests the user account on the DIC.
   */
  public function testUserAccount() {
    $account = $this->drupalCreateUser();
    $this->drupalLogin($account);

    $second_account = $this->drupalCreateUser();

    $this->drupalGet('router_test/test12/' . $second_account->id());
    $this->assertText($account->getUsername() . ':' . $second_account->getUsername());
    $this->assertEqual($account->id(), $this->loggedInUser->id(), 'Ensure that the user was not changed.');

    $this->drupalGet('router_test/test13/' . $second_account->id());
    $this->assertText($account->getUsername() . ':' . $second_account->getUsername());
    $this->assertEqual($account->id(), $this->loggedInUser->id(), 'Ensure that the user was not changed.');
  }

247 248 249 250 251 252
  /**
   * Checks that an ajax request gets rendered as an Ajax response, by mime.
   */
  public function testControllerResolutionAjax() {
    // This will fail with a JSON parse error if the request is not routed to
    // The correct controller.
253
    $this->drupalGetAjax('/router_test/test10');
254 255 256 257 258

    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/json', 'Correct mime content type was returned');

    $this->assertRaw('abcde', 'Correct body was found.');
  }
259 260 261 262

  /**
   * Tests that routes no longer exist for a module that has been uninstalled.
   */
263
  public function testRouterUninstallInstall() {
264
    \Drupal::service('module_installer')->uninstall(array('router_test'));
265
    \Drupal::service('router.builder')->rebuild();
266 267 268 269 270 271 272 273
    try {
      \Drupal::service('router.route_provider')->getRouteByName('router_test.1');
      $this->fail('Route was delete on uninstall.');
    }
    catch (RouteNotFoundException $e) {
      $this->pass('Route was delete on uninstall.');
    }
    // Install the module again.
274
    \Drupal::service('module_installer')->install(array('router_test'));
275
    \Drupal::service('router.builder')->rebuild();
276 277
    $route = \Drupal::service('router.route_provider')->getRouteByName('router_test.1');
    $this->assertNotNull($route, 'Route exists after module installation');
278
  }
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297

  /**
   * Ensure that multiple leading slashes are redirected.
   */
  public function testLeadingSlashes() {
    $request = $this->container->get('request_stack')->getCurrentRequest();
    $url = $request->getUriForPath('//router_test/test1');
    $this->drupalGet($url);
    $this->assertEqual(1, $this->redirectCount, $url . " redirected to " . $this->url);
    $this->assertUrl($request->getUriForPath('/router_test/test1'));

    // It should not matter how many leading slashes are used and query strings
    // should be preserved.
    $url = $request->getUriForPath('/////////////////////////////////////////////////router_test/test1') . '?qs=test';
    $this->drupalGet($url);
    $this->assertEqual(1, $this->redirectCount, $url . " redirected to " . $this->url);
    $this->assertUrl($request->getUriForPath('/router_test/test1') . '?qs=test');
  }

298
}