Updated to Drupal 8.6.4, which is PHP 7.3 friendly. Also updated HTMLaw library....
[yaffs-website] / web / core / modules / system / tests / src / Functional / Routing / RouterTest.php
1 <?php
2
3 namespace Drupal\Tests\system\Functional\Routing;
4
5 use Drupal\Core\Cache\Cache;
6 use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
7 use Drupal\Core\Language\LanguageInterface;
8 use Drupal\Tests\BrowserTestBase;
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 class RouterTest extends BrowserTestBase {
18
19   /**
20    * Modules to enable.
21    *
22    * @var array
23    */
24   public static $modules = ['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     $this->drupalGet('router_test/test1');
35     $this->assertRaw('test1', 'The correct string was returned because the route was successful.');
36     // Check expected headers from FinishResponseSubscriber.
37     $headers = $this->getSession()->getResponseHeaders();
38
39     $this->assertEquals($headers['X-UA-Compatible'], ['IE=edge']);
40     $this->assertEquals($headers['Content-language'], ['en']);
41     $this->assertEquals($headers['X-Content-Type-Options'], ['nosniff']);
42     $this->assertEquals($headers['X-Frame-Options'], ['SAMEORIGIN']);
43
44     $this->drupalGet('router_test/test2');
45     $this->assertRaw('test2', 'The correct string was returned because the route was successful.');
46     // Check expected headers from FinishResponseSubscriber.
47     $headers = $this->drupalGetHeaders();
48     $this->assertEqual($headers['X-Drupal-Cache-Contexts'], [implode(' ', $expected_cache_contexts)]);
49     $this->assertEqual($headers['X-Drupal-Cache-Tags'], ['config:user.role.anonymous http_response rendered']);
50     // Confirm that the page wrapping is being added, so we're not getting a
51     // raw body returned.
52     $this->assertRaw('</html>', 'Page markup was found.');
53     // In some instances, the subrequest handling may get confused and render
54     // a page inception style.  This test verifies that is not happening.
55     $this->assertSession()->responseNotMatches('#</body>.*</body>#s', 'There was no double-page effect from a misrendered subrequest.');
56
57     // Confirm that route-level access check's cacheability is applied to the
58     // X-Drupal-Cache-Contexts and X-Drupal-Cache-Tags headers.
59     // 1. controller result: render array, globally cacheable route access.
60     $this->drupalGet('router_test/test18');
61     $headers = $this->drupalGetHeaders();
62     $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     // 2. controller result: render array, per-role cacheable route access.
65     $this->drupalGet('router_test/test19');
66     $headers = $this->drupalGetHeaders();
67     $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     // 3. controller result: Response object, globally cacheable route access.
70     $this->drupalGet('router_test/test1');
71     $headers = $this->drupalGetHeaders();
72     $this->assertFalse(isset($headers['X-Drupal-Cache-Contexts']));
73     $this->assertFalse(isset($headers['X-Drupal-Cache-Tags']));
74     // 4. controller result: Response object, per-role cacheable route access.
75     $this->drupalGet('router_test/test20');
76     $headers = $this->drupalGetHeaders();
77     $this->assertFalse(isset($headers['X-Drupal-Cache-Contexts']));
78     $this->assertFalse(isset($headers['X-Drupal-Cache-Tags']));
79     // 5. controller result: CacheableResponse object, globally cacheable route access.
80     $this->drupalGet('router_test/test21');
81     $headers = $this->drupalGetHeaders();
82     $this->assertEqual($headers['X-Drupal-Cache-Contexts'], ['']);
83     $this->assertEqual($headers['X-Drupal-Cache-Tags'], ['http_response']);
84     // 6. controller result: CacheableResponse object, per-role cacheable route access.
85     $this->drupalGet('router_test/test22');
86     $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     // Finally, verify that the X-Drupal-Cache-Contexts and X-Drupal-Cache-Tags
91     // headers are not sent when their container parameter is set to FALSE.
92     $this->drupalGet('router_test/test18');
93     $headers = $this->drupalGetHeaders();
94     $this->assertTrue(isset($headers['X-Drupal-Cache-Contexts']));
95     $this->assertTrue(isset($headers['X-Drupal-Cache-Tags']));
96     $this->setContainerParameter('http.response.debug_cacheability_headers', FALSE);
97     $this->rebuildContainer();
98     $this->resetAll();
99     $this->drupalGet('router_test/test18');
100     $headers = $this->drupalGetHeaders();
101     $this->assertFalse(isset($headers['X-Drupal-Cache-Contexts']));
102     $this->assertFalse(isset($headers['X-Drupal-Cache-Tags']));
103   }
104
105   /**
106    * Confirms that multiple routes with the same path do not cause an error.
107    */
108   public function testDuplicateRoutePaths() {
109     // Tests two routes with exactly the same path. The route with the maximum
110     // fit and lowest sorting route name will match, regardless of the order the
111     // routes are declared.
112     // @see \Drupal\Core\Routing\RouteProvider::getRoutesByPath()
113     $this->drupalGet('router-test/duplicate-path2');
114     $this->assertResponse(200);
115     $this->assertRaw('router_test.two_duplicate1');
116
117     // Tests three routes with same the path. One of the routes the path has a
118     // different case.
119     $this->drupalGet('router-test/case-sensitive-duplicate-path3');
120     $this->assertResponse(200);
121     $this->assertRaw('router_test.case_sensitive_duplicate1');
122     // While case-insensitive matching works, exact matches are preferred.
123     $this->drupalGet('router-test/case-sensitive-Duplicate-PATH3');
124     $this->assertResponse(200);
125     $this->assertRaw('router_test.case_sensitive_duplicate2');
126     // Test that case-insensitive matching works, falling back to the first
127     // route defined.
128     $this->drupalGet('router-test/case-sensitive-Duplicate-Path3');
129     $this->assertResponse(200);
130     $this->assertRaw('router_test.case_sensitive_duplicate1');
131   }
132
133   /**
134    * Confirms that placeholders in paths work correctly.
135    */
136   public function testControllerPlaceholders() {
137     // Test with 0 and a random value.
138     $values = ["0", $this->randomMachineName()];
139     foreach ($values as $value) {
140       $this->drupalGet('router_test/test3/' . $value);
141       $this->assertResponse(200);
142       $this->assertRaw($value, 'The correct string was returned because the route was successful.');
143     }
144
145     // Confirm that the page wrapping is being added, so we're not getting a
146     // raw body returned.
147     $this->assertRaw('</html>', 'Page markup was found.');
148
149     // In some instances, the subrequest handling may get confused and render
150     // a page inception style.  This test verifies that is not happening.
151     $this->assertSession()->responseNotMatches('#</body>.*</body>#s', 'There was no double-page effect from a misrendered subrequest.');
152   }
153
154   /**
155    * Confirms that default placeholders in paths work correctly.
156    */
157   public function testControllerPlaceholdersDefaultValues() {
158     $this->drupalGet('router_test/test4');
159     $this->assertResponse(200);
160     $this->assertRaw('narf', 'The correct string was returned because the route was successful.');
161
162     // Confirm that the page wrapping is being added, so we're not getting a
163     // raw body returned.
164     $this->assertRaw('</html>', 'Page markup was found.');
165
166     // In some instances, the subrequest handling may get confused and render
167     // a page inception style.  This test verifies that is not happening.
168     $this->assertSession()->responseNotMatches('#</body>.*</body>#s', 'There was no double-page effect from a misrendered subrequest.');
169   }
170
171   /**
172    * Confirms that default placeholders in paths work correctly.
173    */
174   public function testControllerPlaceholdersDefaultValuesProvided() {
175     $this->drupalGet('router_test/test4/barf');
176     $this->assertResponse(200);
177     $this->assertRaw('barf', 'The correct string was returned because the route was successful.');
178
179     // Confirm that the page wrapping is being added, so we're not getting a
180     // raw body returned.
181     $this->assertRaw('</html>', 'Page markup was found.');
182
183     // In some instances, the subrequest handling may get confused and render
184     // a page inception style.  This test verifies that is not happening.
185     $this->assertSession()->responseNotMatches('#</body>.*</body>#s', 'There was no double-page effect from a misrendered subrequest.');
186   }
187
188   /**
189    * Checks that dynamically defined and altered routes work correctly.
190    *
191    * @see \Drupal\router_test\RouteSubscriber
192    */
193   public function testDynamicRoutes() {
194     // Test the altered route.
195     $this->drupalGet('router_test/test6');
196     $this->assertResponse(200);
197     $this->assertRaw('test5', 'The correct string was returned because the route was successful.');
198   }
199
200   /**
201    * Checks that a request with text/html response gets rendered as a page.
202    */
203   public function testControllerResolutionPage() {
204     $this->drupalGet('/router_test/test10');
205
206     $this->assertRaw('abcde', 'Correct body was found.');
207
208     // Confirm that the page wrapping is being added, so we're not getting a
209     // raw body returned.
210     $this->assertRaw('</html>', 'Page markup was found.');
211
212     // In some instances, the subrequest handling may get confused and render
213     // a page inception style. This test verifies that is not happening.
214     $this->assertSession()->responseNotMatches('#</body>.*</body>#s', 'There was no double-page effect from a misrendered subrequest.');
215   }
216
217   /**
218    * Checks the generate method on the url generator using the front router.
219    */
220   public function testUrlGeneratorFront() {
221     $front_url = Url::fromRoute('<front>', [], ['absolute' => TRUE]);
222     // Compare to the site base URL.
223     $base_url = Url::fromUri('base:/', ['absolute' => TRUE]);
224     $this->assertIdentical($base_url->toString(), $front_url->toString());
225   }
226
227   /**
228    * Tests that a page trying to match a path will succeed.
229    */
230   public function testRouterMatching() {
231     $this->drupalGet('router_test/test14/1');
232     $this->assertResponse(200);
233     $this->assertText('User route "entity.user.canonical" was matched.');
234
235     // Try to match a route for a non-existent user.
236     $this->drupalGet('router_test/test14/2');
237     $this->assertResponse(200);
238     $this->assertText('Route not matched.');
239
240     // Check that very long paths don't cause an error.
241     $path = 'router_test/test1';
242     $suffix = '/d/r/u/p/a/l';
243     for ($i = 0; $i < 10; $i++) {
244       $path .= $suffix;
245       $this->drupalGet($path);
246       $this->assertResponse(404);
247     }
248   }
249
250   /**
251    * Tests that a PSR-7 response works.
252    */
253   public function testRouterResponsePsr7() {
254     $this->drupalGet('/router_test/test23');
255     $this->assertResponse(200);
256     $this->assertText('test23');
257   }
258
259   /**
260    * Tests the user account on the DIC.
261    */
262   public function testUserAccount() {
263     $account = $this->drupalCreateUser();
264     $this->drupalLogin($account);
265
266     $second_account = $this->drupalCreateUser();
267
268     $this->drupalGet('router_test/test12/' . $second_account->id());
269     $this->assertText($account->getUsername() . ':' . $second_account->getUsername());
270     $this->assertEqual($account->id(), $this->loggedInUser->id(), 'Ensure that the user was not changed.');
271
272     $this->drupalGet('router_test/test13/' . $second_account->id());
273     $this->assertText($account->getUsername() . ':' . $second_account->getUsername());
274     $this->assertEqual($account->id(), $this->loggedInUser->id(), 'Ensure that the user was not changed.');
275   }
276
277   /**
278    * Checks that an ajax request gets rendered as an Ajax response, by mime.
279    */
280   public function testControllerResolutionAjax() {
281     // This will fail with a JSON parse error if the request is not routed to
282     // The correct controller.
283     $options['query'][MainContentViewSubscriber::WRAPPER_FORMAT] = 'drupal_ajax';
284     $headers[] = 'X-Requested-With: XMLHttpRequest';
285     $this->drupalGet('/router_test/test10', $options, $headers);
286
287     $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/json', 'Correct mime content type was returned');
288
289     $this->assertRaw('abcde', 'Correct body was found.');
290   }
291
292   /**
293    * Tests that routes no longer exist for a module that has been uninstalled.
294    */
295   public function testRouterUninstallInstall() {
296     \Drupal::service('module_installer')->uninstall(['router_test']);
297     \Drupal::service('router.builder')->rebuild();
298     try {
299       \Drupal::service('router.route_provider')->getRouteByName('router_test.1');
300       $this->fail('Route was delete on uninstall.');
301     }
302     catch (RouteNotFoundException $e) {
303       $this->pass('Route was delete on uninstall.');
304     }
305     // Install the module again.
306     \Drupal::service('module_installer')->install(['router_test']);
307     \Drupal::service('router.builder')->rebuild();
308     $route = \Drupal::service('router.route_provider')->getRouteByName('router_test.1');
309     $this->assertNotNull($route, 'Route exists after module installation');
310   }
311
312   /**
313    * Ensure that multiple leading slashes are redirected.
314    */
315   public function testLeadingSlashes() {
316     $request = $this->container->get('request_stack')->getCurrentRequest();
317     $url = $request->getUriForPath('//router_test/test1');
318     $this->drupalGet($url);
319     $this->assertUrl($request->getUriForPath('/router_test/test1'));
320
321     // It should not matter how many leading slashes are used and query strings
322     // should be preserved.
323     $url = $request->getUriForPath('/////////////////////////////////////////////////router_test/test1') . '?qs=test';
324     $this->drupalGet($url);
325     $this->assertUrl($request->getUriForPath('/router_test/test1') . '?qs=test');
326
327     // Ensure that external URLs in destination query params are not redirected
328     // to.
329     $url = $request->getUriForPath('/////////////////////////////////////////////////router_test/test1') . '?qs=test&destination=http://www.example.com%5c@drupal8alt.test';
330     $this->drupalGet($url);
331     $this->assertUrl($request->getUriForPath('/router_test/test1') . '?qs=test');
332   }
333
334 }