3 namespace Drupal\redirect\Tests;
5 use Drupal\Component\Utility\SafeMarkup;
6 use Drupal\Core\Language\Language;
7 use Drupal\simpletest\WebTestBase;
8 use Drupal\language\Entity\ConfigurableLanguage;
11 * Global redirect test cases.
15 class GlobalRedirectTest extends WebTestBase {
22 public static $modules = [
30 'content_translation',
34 * @var \Drupal\Core\Session\AccountInterface
36 protected $normalUser;
39 * @var \Drupal\Core\Session\AccountInterface
44 * @var \Drupal\Core\Config\Config
49 * @var \Drupal\Core\Entity\ContentEntityInterface
54 * @var \Drupal\Core\Entity\ContentEntityInterface
59 * @var \Drupal\Core\Entity\ContentEntityInterface
66 protected function setUp() {
69 $this->config = $this->config('redirect.settings');
71 $this->drupalCreateContentType(['type' => 'page', 'name' => 'Page']);
72 $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
74 // Create a users for testing the access.
75 $this->normalUser = $this->drupalCreateUser([
77 'create page content',
79 'access administration pages',
81 $this->adminUser = $this->drupalCreateUser([
82 'administer site configuration',
83 'access administration pages',
84 'administer languages',
85 'administer content types',
86 'administer content translation',
87 'create page content',
88 'edit own page content',
89 'create content translations',
93 $this->node = $this->drupalCreateNode([
95 'title' => 'Test Page Node',
96 'path' => ['alias' => '/test-node'],
97 'language' => Language::LANGCODE_NOT_SPECIFIED,
100 // Create an alias for the create story path - this is used in the
101 // "redirect with permissions testing" test.
102 \Drupal::service('path.alias_storage')->save('/admin/config/system/site-information', '/site-info');
104 // Create a taxonomy term for the forum.
105 $term = entity_create('taxonomy_term', [
106 'name' => 'Test Forum Term',
108 'langcode' => Language::LANGCODE_NOT_SPECIFIED,
111 $this->forumTerm = $term;
113 // Create another taxonomy vocabulary with a term.
114 $vocab = entity_create('taxonomy_vocabulary', [
115 'name' => 'test vocab',
116 'vid' => 'test-vocab',
117 'langcode' => Language::LANGCODE_NOT_SPECIFIED,
120 $term = entity_create('taxonomy_term', [
121 'name' => 'Test Term',
122 'vid' => $vocab->id(),
123 'langcode' => Language::LANGCODE_NOT_SPECIFIED,
124 'path' => ['alias' => '/test-term'],
132 * Will test the redirects.
134 public function testRedirects() {
136 // First test that the good stuff can be switched off.
137 $this->config->set('route_normalizer_enabled', FALSE)->save();
138 $this->assertRedirect('index.php/node/' . $this->node->id(), NULL, 'HTTP/1.1 200 OK');
139 $this->assertRedirect('index.php/test-node', NULL, 'HTTP/1.1 200 OK');
140 $this->assertRedirect('test-node/', NULL, 'HTTP/1.1 200 OK');
141 $this->assertRedirect('Test-node/', NULL, 'HTTP/1.1 200 OK');
143 $this->config->set('route_normalizer_enabled', TRUE)->save();
145 // Test alias normalization.
146 $this->assertRedirect('node/' . $this->node->id(), 'test-node');
147 $this->assertRedirect('Test-node', 'test-node');
149 // Test redirects for non-clean urls.
150 $this->assertRedirect('index.php/node/' . $this->node->id(), 'test-node');
151 $this->assertRedirect('index.php/test-node', 'test-node');
154 $this->assertRedirect('test-node/', 'test-node');
156 // Test front page redirects.
157 $this->config('system.site')->set('page.front', '/node')->save();
158 $this->assertRedirect('node', '<front>');
160 // Test front page redirects with an alias.
161 \Drupal::service('path.alias_storage')->save('/node', '/node-alias');
162 $this->assertRedirect('node-alias', '<front>');
164 // Test post request.
165 $this->drupalPost('Test-node', 'application/json', array());
166 // Does not do a redirect, stays in the same path.
167 $this->assertEqual(basename($this->getUrl()), 'Test-node');
169 // Test the access checking.
170 $this->config->set('access_check', TRUE)->save();
171 $this->assertRedirect('admin/config/system/site-information', NULL, 'HTTP/1.1 403 Forbidden');
173 $this->config->set('access_check', FALSE)->save();
174 // @todo - here it seems that the access check runs prior to our redirecting
175 // check why so and enable the test.
176 //$this->assertRedirect('admin/config/system/site-information', 'site-info');
178 // Test original query string is preserved with alias normalization.
179 $this->assertRedirect('Test-node?&foo&.bar=baz', 'test-node?&foo&.bar=baz');
181 // Test alias normalization with trailing ?.
182 $this->assertRedirect('test-node?', 'test-node');
183 $this->assertRedirect('Test-node?', 'test-node');
185 // Test alias normalization still works without trailing ?.
186 $this->assertRedirect('test-node', NULL, 'HTTP/1.1 200 OK');
187 $this->assertRedirect('Test-node', 'test-node');
189 // Login as user with admin privileges.
190 $this->drupalLogin($this->adminUser);
192 // Test ignoring admin paths.
193 $this->config->set('ignore_admin_path', FALSE)->save();
194 $this->assertRedirect('admin/config/system/site-information', 'site-info');
196 // Test alias normalization again with ignore_admin_path false.
197 $this->assertRedirect('Test-node', 'test-node');
199 $this->config->set('ignore_admin_path', TRUE)->save();
200 $this->assertRedirect('admin/config/system/site-information', NULL, 'HTTP/1.1 200 OK');
202 // Test alias normalization again with ignore_admin_path true.
203 $this->assertRedirect('Test-node', 'test-node');
207 * Test that redirects work properly with content_translation enabled.
209 public function testLanguageRedirects() {
210 $this->drupalLogin($this->adminUser);
212 // Add a new language.
213 ConfigurableLanguage::createFromLangcode('es')
216 // Enable URL language detection and selection.
217 $edit = ['language_interface[enabled][language-url]' => '1'];
218 $this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
220 // Set page content type to use multilingual support.
222 'language_configuration[language_alterable]' => TRUE,
223 'language_configuration[content_translation]' => TRUE,
225 $this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
226 $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Page')), 'Basic page content type has been updated.');
228 $spanish_node = $this->drupalCreateNode([
230 'title' => 'Spanish Test Page Node',
231 'path' => ['alias' => '/spanish-test-node'],
235 // Test multilingual redirect.
236 $this->assertRedirect('es/node/' . $spanish_node->id(), 'es/spanish-test-node');
240 * Asserts the redirect from $path to the $expected_ending_url.
242 * @param string $path
244 * @param $expected_ending_url
245 * The path where we expect it to redirect. If NULL value provided, no
246 * redirect is expected.
247 * @param string $expected_ending_status
248 * The status we expect to get with the first request.
250 public function assertRedirect($path, $expected_ending_url, $expected_ending_status = 'HTTP/1.1 301 Moved Permanently') {
251 $this->drupalHead($GLOBALS['base_url'] . '/' . $path);
252 $headers = $this->drupalGetHeaders(TRUE);
254 $ending_url = isset($headers[0]['location']) ? $headers[0]['location'] : NULL;
255 $message = SafeMarkup::format('Testing redirect from %from to %to. Ending url: %url', array(
257 '%to' => $expected_ending_url,
258 '%url' => $ending_url,
262 if ($expected_ending_url == '<front>') {
263 $expected_ending_url = $GLOBALS['base_url'] . '/';
265 elseif (!empty($expected_ending_url)) {
266 $expected_ending_url = $GLOBALS['base_url'] . '/' . $expected_ending_url;
269 $expected_ending_url = NULL;
272 $this->assertEqual($expected_ending_url, $ending_url);
274 $this->assertEqual($headers[0][':status'], $expected_ending_status);
280 protected function drupalHead($path, array $options = [], array $headers = []) {
281 // Always just use getAbsolutePath() so that generating the link does not
282 // alter special requests.
283 $url = $this->getAbsoluteUrl($path);
284 $out = $this->curlExec([CURLOPT_NOBODY => TRUE, CURLOPT_URL => $url, CURLOPT_HTTPHEADER => $headers]);
285 // Ensure that any changes to variables in the other thread are picked up.
286 $this->refreshVariables();
288 if ($this->dumpHeaders) {
289 $this->verbose('GET request to: ' . $path .
290 '<hr />Ending URL: ' . $this->getUrl() .
291 '<hr />Headers: <pre>' . Html::escape(var_export(array_map('trim', $this->headers), TRUE)) . '</pre>');