path.test 19.7 KB
Newer Older
1 2
<?php

3 4
/**
 * @file
5
 * Tests for path.module.
6 7
 */

8
class PathTestCase extends DrupalWebTestCase {
9
  public static function getInfo() {
10
    return array(
11 12 13
      'name' => 'Path alias functionality',
      'description' => 'Add, edit, delete, and change alias and verify its consistency in the database.',
      'group' => 'Path',
14 15 16 17 18
    );
  }

  function setUp() {
    parent::setUp('path');
19 20 21

    // Create test user and login.
    $web_user = $this->drupalCreateUser(array('create page content', 'edit own page content', 'administer url aliases', 'create url aliases'));
22 23 24
    $this->drupalLogin($web_user);
  }

25 26 27 28 29 30 31 32 33
  /**
   * Test the path cache.
   */
  function testPathCache() {
    // Create test node.
    $node1 = $this->drupalCreateNode();

    // Create alias.
    $edit = array();
34 35
    $edit['source'] = 'node/' . $node1->nid;
    $edit['alias'] = $this->randomName(8);
36
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
37 38 39

    // Visit the system path for the node and confirm a cache entry is
    // created.
40
    cache('path')->flush();
41
    $this->drupalGet($edit['source']);
42
    $this->assertTrue(cache('path')->get($edit['source']), t('Cache entry was created.'));
43 44

    // Visit the alias for the node and confirm a cache entry is created.
45
    cache('path')->flush();
46
    $this->drupalGet($edit['alias']);
47
    $this->assertTrue(cache('path')->get($edit['source']), t('Cache entry was created.'));
48 49
  }

50 51 52 53
  /**
   * Test alias functionality through the admin interfaces.
   */
  function testAdminAlias() {
54
    // Create test node.
55
    $node1 = $this->drupalCreateNode();
56

57
    // Create alias.
58
    $edit = array();
59 60
    $edit['source'] = 'node/' . $node1->nid;
    $edit['alias'] = $this->randomName(8);
61
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
62

63
    // Confirm that the alias works.
64
    $this->drupalGet($edit['alias']);
65
    $this->assertText($node1->title, 'Alias works.');
66
    $this->assertResponse(200);
67

68
    // Change alias to one containing "exotic" characters.
69
    $pid = $this->getPID($edit['alias']);
70

71
    $previous = $edit['alias'];
72 73 74
    $edit['alias'] = "- ._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters.
      "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
      "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
75
    $this->drupalPost('admin/config/search/path/edit/' . $pid, $edit, t('Save'));
76

77
    // Confirm that the alias works.
78
    $this->drupalGet($edit['alias']);
79
    $this->assertText($node1->title, 'Changed alias works.');
80
    $this->assertResponse(200);
81

82
    drupal_static_reset('drupal_lookup_path');
83
    // Confirm that previous alias no longer works.
84 85 86 87
    $this->drupalGet($previous);
    $this->assertNoText($node1->title, 'Previous alias no longer works.');
    $this->assertResponse(404);

88
    // Create second test node.
89
    $node2 = $this->drupalCreateNode();
90

91
    // Set alias to second test node.
92 93
    $edit['source'] = 'node/' . $node2->nid;
    // leave $edit['alias'] the same
94
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
95

96
    // Confirm no duplicate was created.
97
    $this->assertRaw(t('The alias %alias is already in use in this language.', array('%alias' => $edit['alias'])), 'Attempt to move alias was rejected.');
98

99
    // Delete alias.
100 101
    $this->drupalPost('admin/config/search/path/edit/' . $pid, array(), t('Delete'));
    $this->drupalPost(NULL, array(), t('Confirm'));
102

103
    // Confirm that the alias no longer works.
104
    $this->drupalGet($edit['alias']);
105
    $this->assertNoText($node1->title, 'Alias was successfully deleted.');
106
    $this->assertResponse(404);
107 108 109 110 111 112
  }

  /**
   * Test alias functionality through the node interfaces.
   */
  function testNodeAlias() {
113
    // Create test node.
114
    $node1 = $this->drupalCreateNode();
115

116
    // Create alias.
117
    $edit = array();
118
    $edit['path[alias]'] = $this->randomName(8);
119 120
    $this->drupalPost('node/' . $node1->nid . '/edit', $edit, t('Save'));

121
    // Confirm that the alias works.
122
    $this->drupalGet($edit['path[alias]']);
123
    $this->assertText($node1->title, 'Alias works.');
124
    $this->assertResponse(200);
125

126
    // Change alias to one containing "exotic" characters.
127
    $previous = $edit['path[alias]'];
128 129 130
    $edit['path[alias]'] = "- ._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters.
      "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
      "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
131 132
    $this->drupalPost('node/' . $node1->nid . '/edit', $edit, t('Save'));

133
    // Confirm that the alias works.
134
    $this->drupalGet($edit['path[alias]']);
135
    $this->assertText($node1->title, 'Changed alias works.');
136
    $this->assertResponse(200);
137

138
    // Make sure that previous alias no longer works.
139
    $this->drupalGet($previous);
140
    $this->assertNoText($node1->title, 'Previous alias no longer works.');
141 142
    $this->assertResponse(404);

143
    // Create second test node.
144
    $node2 = $this->drupalCreateNode();
145

146
    // Set alias to second test node.
147
    // Leave $edit['path[alias]'] the same.
148 149
    $this->drupalPost('node/' . $node2->nid . '/edit', $edit, t('Save'));

150
    // Confirm that the alias didn't make a duplicate.
151
    $this->assertText(t('The alias is already in use.'), 'Attempt to moved alias was rejected.');
152

153
    // Delete alias.
154
    $this->drupalPost('node/' . $node1->nid . '/edit', array('path[alias]' => ''), t('Save'));
155

156
    // Confirm that the alias no longer works.
157
    $this->drupalGet($edit['path[alias]']);
158
    $this->assertNoText($node1->title, 'Alias was successfully deleted.');
159
    $this->assertResponse(404);
160 161
  }

162 163
  function getPID($alias) {
    return db_query("SELECT pid FROM {url_alias} WHERE alias = :alias", array(':alias' => $alias))->fetchField();
164
  }
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181

  /**
   * Tests that duplicate aliases fail validation.
   */
  function testDuplicateNodeAlias() {
    // Create one node with a random alias.
    $node_one = $this->drupalCreateNode();
    $edit = array();
    $edit['path[alias]'] = $this->randomName();
    $this->drupalPost('node/' . $node_one->nid . '/edit', $edit, t('Save'));

    // Now create another node and try to set the same alias.
    $node_two = $this->drupalCreateNode();
    $this->drupalPost('node/' . $node_two->nid . '/edit', $edit, t('Save'));
    $this->assertText(t('The alias is already in use.'));
    $this->assertFieldByXPath("//input[@name='path[alias]' and contains(@class, 'error')]", $edit['path[alias]'], 'Textfield exists and has the error class.');
  }
182
}
183

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
/**
 * Test URL aliases for taxonomy terms.
 */
class PathTaxonomyTermTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Taxonomy term URL aliases',
      'description' => 'Tests URL aliases for taxonomy terms.',
      'group' => 'Path',
    );
  }

  function setUp() {
    parent::setUp('path', 'taxonomy');

    // Create and login user.
    $web_user = $this->drupalCreateUser(array('administer url aliases', 'administer taxonomy', 'access administration pages'));
    $this->drupalLogin($web_user);
  }

  /**
   * Test alias functionality through the admin interfaces.
   */
  function testTermAlias() {
    // Create a term in the default 'Tags' vocabulary with URL alias.
209
    $vocabulary = taxonomy_vocabulary_load(1);
210
    $description = $this->randomName();;
211 212
    $edit = array();
    $edit['name'] = $this->randomName();
213
    $edit['description[value]'] = $description;
214
    $edit['path[alias]'] = $this->randomName();
215
    $this->drupalPost('admin/structure/taxonomy/' . $vocabulary->machine_name . '/add', $edit, t('Save'));
216 217 218

    // Confirm that the alias works.
    $this->drupalGet($edit['path[alias]']);
219
    $this->assertText($description, 'Term can be accessed on URL alias.');
220 221 222 223 224 225 226 227 228

    // Change the term's URL alias.
    $tid = db_query("SELECT tid FROM {taxonomy_term_data} WHERE name = :name", array(':name' => $edit['name']))->fetchField();
    $edit2 = array();
    $edit2['path[alias]'] = $this->randomName();
    $this->drupalPost('taxonomy/term/' . $tid . '/edit', $edit2, t('Save'));

    // Confirm that the changed alias works.
    $this->drupalGet($edit2['path[alias]']);
229
    $this->assertText($description, 'Term can be accessed on changed URL alias.');
230 231 232

    // Confirm that the old alias no longer works.
    $this->drupalGet($edit['path[alias]']);
233
    $this->assertNoText($description, 'Old URL alias has been removed after altering.');
234 235 236 237 238 239 240 241 242
    $this->assertResponse(404, 'Old URL alias returns 404.');

    // Remove the term's URL alias.
    $edit3 = array();
    $edit3['path[alias]'] = '';
    $this->drupalPost('taxonomy/term/' . $tid . '/edit', $edit3, t('Save'));

    // Confirm that the alias no longer works.
    $this->drupalGet($edit2['path[alias]']);
243
    $this->assertNoText($description, 'Old URL alias has been removed after altering.');
244 245 246 247
    $this->assertResponse(404, 'Old URL alias returns 404.');
  }
}

248
class PathLanguageTestCase extends DrupalWebTestCase {
249
  public static function getInfo() {
250
    return array(
251 252 253
      'name' => 'Path aliases with translated nodes',
      'description' => 'Confirm that paths work with translated nodes',
      'group' => 'Path',
254 255 256 257 258 259 260
    );
  }

  function setUp() {
    parent::setUp('path', 'locale', 'translation');

    // Create and login user.
261 262
    $this->web_user = $this->drupalCreateUser(array('edit any page content', 'create page content', 'administer url aliases', 'create url aliases', 'administer languages', 'translate content', 'access administration pages'));
    $this->drupalLogin($this->web_user);
263 264 265

    // Enable French language.
    $edit = array();
266
    $edit['predefined_langcode'] = 'fr';
267

268
    $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
269

270
    // Enable URL language detection and selection.
271
    $edit = array('language_interface[enabled][locale-url]' => 1);
272
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
273 274 275 276 277 278 279
  }

  /**
   * Test alias functionality through the admin interfaces.
   */
  function testAliasTranslation() {
    // Set 'page' content type to enable translation.
280
    variable_set('node_type_language_page', TRANSLATION_ENABLED);
281

282
    $english_node = $this->drupalCreateNode(array('type' => 'page'));
283
    $english_alias = $this->randomName();
284 285

    // Edit the node to set language and path.
286
    $edit = array();
287
    $edit['langcode'] = 'en';
288
    $edit['path[alias]'] = $english_alias;
289
    $this->drupalPost('node/' . $english_node->nid . '/edit', $edit, t('Save'));
290 291

    // Confirm that the alias works.
292
    $this->drupalGet($english_alias);
293
    $this->assertText($english_node->title, 'Alias works.');
294 295 296 297 298

    // Translate the node into French.
    $this->drupalGet('node/' . $english_node->nid . '/translate');
    $this->clickLink(t('add translation'));
    $edit = array();
299
    $langcode = LANGUAGE_NONE;
300
    $edit["title"] = $this->randomName();
301
    $edit["body[$langcode][0][value]"] = $this->randomName();
302 303
    $french_alias = $this->randomName();
    $edit['path[alias]'] = $french_alias;
304 305 306 307 308 309
    $this->drupalPost(NULL, $edit, t('Save'));

    // Clear the path lookup cache.
    drupal_lookup_path('wipe');

    // Ensure the node was created.
310
    $french_node = $this->drupalGetNodeByTitle($edit["title"]);
311 312 313
    $this->assertTrue(($french_node), 'Node found in database.');

    // Confirm that the alias works.
314
    $this->drupalGet('fr/' . $edit['path[alias]']);
315
    $this->assertText($french_node->title, 'Alias for French translation works.');
316

317 318
    // Confirm that the alias is returned by url(). Languages are cached on
    // many levels, and we need to clear those caches.
319
    drupal_static_reset('language_list');
320
    drupal_static_reset('locale_url_outbound_alter');
321
    drupal_static_reset('locale_language_url_rewrite_url');
322
    $languages = language_list();
323
    $url = url('node/' . $french_node->nid, array('language' => $languages[$french_node->langcode]));
324
    $this->assertTrue(strpos($url, $edit['path[alias]']), t('URL contains the path alias.'));
325 326 327 328

    // Confirm that the alias works even when changing language negotiation
    // options. Enable User language detection and selection over URL one.
    $edit = array(
329 330 331 332
      'language_interface[enabled][locale-user]' => 1,
      'language_interface[weight][locale-user]' => -9,
      'language_interface[enabled][locale-url]' => 1,
      'language_interface[weight][locale-url]' => -8,
333
    );
334
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
335 336

    // Change user language preference.
337
    $edit = array('preferred_langcode' => 'fr');
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
    $this->drupalPost("user/{$this->web_user->uid}/edit", $edit, t('Save'));

    // Check that the English alias works. In this situation French is the
    // current UI and content language, while URL language is English (since we
    // do not have a path prefix we fall back to the site's default language).
    // We need to ensure that the user language preference is not taken into
    // account while determining the path alias language, because if this
    // happens we have no way to check that the path alias is valid: there is no
    // path alias for French matching the english alias. So drupal_lookup_path()
    // needs to use the URL language to check whether the alias is valid.
    $this->drupalGet($english_alias);
    $this->assertText($english_node->title, 'Alias for English translation works.');

    // Check that the French alias works.
    $this->drupalGet("fr/$french_alias");
    $this->assertText($french_node->title, 'Alias for French translation works.');

    // Disable URL language negotiation.
356
    $edit = array('language_interface[enabled][locale-url]' => FALSE);
357
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
358 359 360 361 362 363 364 365 366 367

    // Check that the English alias still works.
    $this->drupalGet($english_alias);
    $this->assertText($english_node->title, 'Alias for English translation works.');

    // Check that the French alias is not available. We check the unprefixed
    // alias because we disabled URL language negotiation above. In this
    // situation only aliases in the default language and language neutral ones
    // should keep working.
    $this->drupalGet($french_alias);
368
    $this->assertResponse(404, t('Alias for French translation is unavailable when URL language negotiation is disabled.'));
369 370 371 372

    // drupal_lookup_path() has an internal static cache. Check to see that
    // it has the appropriate contents at this point.
    drupal_lookup_path('wipe');
373
    $french_node_path = drupal_lookup_path('source', $french_alias, $french_node->langcode);
374
    $this->assertEqual($french_node_path, 'node/' . $french_node->nid, t('Normal path works.'));
375
    // Second call should return the same path.
376
    $french_node_path = drupal_lookup_path('source', $french_alias, $french_node->langcode);
377
    $this->assertEqual($french_node_path, 'node/' . $french_node->nid, t('Normal path is the same.'));
378 379

    // Confirm that the alias works.
380
    $french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->langcode);
381
    $this->assertEqual($french_node_alias, $french_alias, t('Alias works.'));
382
    // Second call should return the same alias.
383
    $french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->langcode);
384
    $this->assertEqual($french_node_alias, $french_alias, t('Alias is the same.'));
385 386
  }
}
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408

/**
 * Tests the user interface for creating path aliases, with languages.
 */
class PathLanguageUITestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Path aliases with languages',
      'description' => 'Confirm that the Path module user interface works with languages.',
      'group' => 'Path',
    );
  }

  function setUp() {
    parent::setUp('path', 'locale');

    // Create and login user.
    $web_user = $this->drupalCreateUser(array('edit any page content', 'create page content', 'administer url aliases', 'create url aliases', 'administer languages', 'access administration pages'));
    $this->drupalLogin($web_user);

    // Enable French language.
    $edit = array();
409
    $edit['predefined_langcode'] = 'fr';
410 411 412

    $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));

413
    // Enable URL language detection and selection.
414
    $edit = array('language_interface[enabled][locale-url]' => 1);
415
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
  }

  /**
   * Tests that a language-neutral URL alias works.
   */
  function testLanguageNeutralURLs() {
    $name = $this->randomName(8);
    $edit = array();
    $edit['source'] = 'admin/config/search/path';
    $edit['alias'] = $name;
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));

    $this->drupalGet($name);
    $this->assertText(t('Filter aliases'), 'Language-neutral URL alias works');
  }

  /**
   * Tests that a default language URL alias works.
   */
  function testDefaultLanguageURLs() {
    $name = $this->randomName(8);
    $edit = array();
    $edit['source'] = 'admin/config/search/path';
    $edit['alias'] = $name;
440
    $edit['langcode'] = 'en';
441 442 443 444 445 446 447 448 449 450 451 452 453 454
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));

    $this->drupalGet($name);
    $this->assertText(t('Filter aliases'), 'English URL alias works');
  }

  /**
   * Tests that a non-default language URL alias works.
   */
  function testNonDefaultURLs() {
    $name = $this->randomName(8);
    $edit = array();
    $edit['source'] = 'admin/config/search/path';
    $edit['alias'] = $name;
455
    $edit['langcode'] = 'fr';
456 457 458 459 460 461 462
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));

    $this->drupalGet('fr/' . $name);
    $this->assertText(t('Filter aliases'), 'Foreign URL alias works');
  }

}
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484

/**
 * Tests that paths are not prefixed on a monolingual site.
 */
class PathMonolingualTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Paths on non-English monolingual sites',
      'description' => 'Confirm that paths are not changed on monolingual non-English sites',
      'group' => 'Path',
    );
  }

  function setUp() {
    parent::setUp('path', 'locale', 'translation');

    // Create and login user.
    $web_user = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
    $this->drupalLogin($web_user);

    // Enable French language.
    $edit = array();
485
    $edit['predefined_langcode'] = 'fr';
486 487 488 489 490 491 492
    $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));

    // Make French the default language.
    $edit = array('site_default' => 'fr');
    $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));

    // Disable English.
493
    $edit = array('languages[en][enabled]' => FALSE);
494 495 496
    $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));

    // Verify that French is the only language.
497
    $this->assertFalse(language_multilingual(), t('Site is mono-lingual'));
498
    $this->assertEqual(language_default()->langcode, 'fr', t('French is the default language'));
499 500

    // Set language detection to URL.
501
    $edit = array('language_interface[enabled][locale-url]' => TRUE);
502
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523

    // Force languages to be initialized.
    drupal_language_initialize();
  }

  /**
   * Verifies that links do not have language prefixes in them.
   */
  function testPageLinks() {
    // Navigate to 'admin/config' path.
    $this->drupalGet('admin/config');

    // Verify that links in this page do not have a 'fr/' prefix.
    $this->assertNoLinkByHref('/fr/', 'Links do not contain language prefix');

    // Verify that links in this page can be followed and work.
    $this->clickLink(t('Languages'));
    $this->assertResponse(200, 'Clicked link results in a valid page');
    $this->assertText(t('Add language'), 'Page contains the add language text');
  }
}