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

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

8
class PathTestCase extends DrupalWebTestCase {
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
  function setUp() {
    $modules = func_get_args();
    if (isset($modules[0]) && is_array($modules[0])) {
      $modules = $modules[0];
    }
    $modules[] = 'node';
    $modules[] = 'path';
    parent::setUp($modules);

    // Create Basic page and Article node types.
    if ($this->profile != 'standard') {
      $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
      $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
    }
  }
}

class PathAliasTestCase extends PathTestCase {
27
  public static function getInfo() {
28
    return array(
29 30 31
      'name' => 'Path alias functionality',
      'description' => 'Add, edit, delete, and change alias and verify its consistency in the database.',
      'group' => 'Path',
32 33 34 35 36
    );
  }

  function setUp() {
    parent::setUp('path');
37 38 39

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

43 44 45 46 47 48 49 50 51
  /**
   * Test the path cache.
   */
  function testPathCache() {
    // Create test node.
    $node1 = $this->drupalCreateNode();

    // Create alias.
    $edit = array();
52 53
    $edit['source'] = 'node/' . $node1->nid;
    $edit['alias'] = $this->randomName(8);
54
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
55 56 57

    // Visit the system path for the node and confirm a cache entry is
    // created.
58
    cache('path')->flush();
59
    $this->drupalGet($edit['source']);
60
    $this->assertTrue(cache('path')->get($edit['source']), t('Cache entry was created.'));
61 62

    // Visit the alias for the node and confirm a cache entry is created.
63
    cache('path')->flush();
64
    $this->drupalGet($edit['alias']);
65
    $this->assertTrue(cache('path')->get($edit['source']), t('Cache entry was created.'));
66 67
  }

68 69 70 71
  /**
   * Test alias functionality through the admin interfaces.
   */
  function testAdminAlias() {
72
    // Create test node.
73
    $node1 = $this->drupalCreateNode();
74

75
    // Create alias.
76
    $edit = array();
77 78
    $edit['source'] = 'node/' . $node1->nid;
    $edit['alias'] = $this->randomName(8);
79
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
80

81
    // Confirm that the alias works.
82
    $this->drupalGet($edit['alias']);
83
    $this->assertText($node1->title, 'Alias works.');
84
    $this->assertResponse(200);
85

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

89
    $previous = $edit['alias'];
90 91 92
    $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.
93
    $this->drupalPost('admin/config/search/path/edit/' . $pid, $edit, t('Save'));
94

95
    // Confirm that the alias works.
96
    $this->drupalGet($edit['alias']);
97
    $this->assertText($node1->title, 'Changed alias works.');
98
    $this->assertResponse(200);
99

100
    drupal_static_reset('drupal_lookup_path');
101
    // Confirm that previous alias no longer works.
102 103 104 105
    $this->drupalGet($previous);
    $this->assertNoText($node1->title, 'Previous alias no longer works.');
    $this->assertResponse(404);

106
    // Create second test node.
107
    $node2 = $this->drupalCreateNode();
108

109
    // Set alias to second test node.
110 111
    $edit['source'] = 'node/' . $node2->nid;
    // leave $edit['alias'] the same
112
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
113

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

117
    // Delete alias.
118 119
    $this->drupalPost('admin/config/search/path/edit/' . $pid, array(), t('Delete'));
    $this->drupalPost(NULL, array(), t('Confirm'));
120

121
    // Confirm that the alias no longer works.
122
    $this->drupalGet($edit['alias']);
123
    $this->assertNoText($node1->title, 'Alias was successfully deleted.');
124
    $this->assertResponse(404);
125 126 127 128 129 130
  }

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

134
    // Create alias.
135
    $edit = array();
136
    $edit['path[alias]'] = $this->randomName(8);
137 138
    $this->drupalPost('node/' . $node1->nid . '/edit', $edit, t('Save'));

139
    // Confirm that the alias works.
140
    $this->drupalGet($edit['path[alias]']);
141
    $this->assertText($node1->title, 'Alias works.');
142
    $this->assertResponse(200);
143

144
    // Change alias to one containing "exotic" characters.
145
    $previous = $edit['path[alias]'];
146 147 148
    $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.
149 150
    $this->drupalPost('node/' . $node1->nid . '/edit', $edit, t('Save'));

151
    // Confirm that the alias works.
152
    $this->drupalGet($edit['path[alias]']);
153
    $this->assertText($node1->title, 'Changed alias works.');
154
    $this->assertResponse(200);
155

156
    // Make sure that previous alias no longer works.
157
    $this->drupalGet($previous);
158
    $this->assertNoText($node1->title, 'Previous alias no longer works.');
159 160
    $this->assertResponse(404);

161
    // Create second test node.
162
    $node2 = $this->drupalCreateNode();
163

164
    // Set alias to second test node.
165
    // Leave $edit['path[alias]'] the same.
166 167
    $this->drupalPost('node/' . $node2->nid . '/edit', $edit, t('Save'));

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

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

174
    // Confirm that the alias no longer works.
175
    $this->drupalGet($edit['path[alias]']);
176
    $this->assertNoText($node1->title, 'Alias was successfully deleted.');
177
    $this->assertResponse(404);
178 179
  }

180 181
  function getPID($alias) {
    return db_query("SELECT pid FROM {url_alias} WHERE alias = :alias", array(':alias' => $alias))->fetchField();
182
  }
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199

  /**
   * 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.');
  }
200
}
201

202 203 204
/**
 * Test URL aliases for taxonomy terms.
 */
205
class PathTaxonomyTermTestCase extends PathTestCase {
206 207 208 209 210 211 212 213 214
  public static function getInfo() {
    return array(
      'name' => 'Taxonomy term URL aliases',
      'description' => 'Tests URL aliases for taxonomy terms.',
      'group' => 'Path',
    );
  }

  function setUp() {
215 216 217 218 219 220 221 222
    parent::setUp(array('taxonomy'));

    // Create a Tags vocabulary for the Article node type.
    $vocabulary = (object) array(
      'name' => t('Tags'),
      'machine_name' => 'tags',
    );
    taxonomy_vocabulary_save($vocabulary);
223 224 225 226 227 228 229 230 231 232 233

    // 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.
234
    $vocabulary = taxonomy_vocabulary_load(1);
235
    $description = $this->randomName();;
236 237
    $edit = array();
    $edit['name'] = $this->randomName();
238
    $edit['description[value]'] = $description;
239
    $edit['path[alias]'] = $this->randomName();
240
    $this->drupalPost('admin/structure/taxonomy/' . $vocabulary->machine_name . '/add', $edit, t('Save'));
241 242 243

    // Confirm that the alias works.
    $this->drupalGet($edit['path[alias]']);
244
    $this->assertText($description, 'Term can be accessed on URL alias.');
245 246 247 248 249 250 251 252 253

    // 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]']);
254
    $this->assertText($description, 'Term can be accessed on changed URL alias.');
255 256 257

    // Confirm that the old alias no longer works.
    $this->drupalGet($edit['path[alias]']);
258
    $this->assertNoText($description, 'Old URL alias has been removed after altering.');
259 260 261 262 263 264 265 266 267
    $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]']);
268
    $this->assertNoText($description, 'Old URL alias has been removed after altering.');
269 270 271 272
    $this->assertResponse(404, 'Old URL alias returns 404.');
  }
}

273
class PathLanguageTestCase extends PathTestCase {
274
  public static function getInfo() {
275
    return array(
276 277 278
      'name' => 'Path aliases with translated nodes',
      'description' => 'Confirm that paths work with translated nodes',
      'group' => 'Path',
279 280 281 282 283 284 285
    );
  }

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

    // Create and login user.
286 287
    $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);
288 289 290

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

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

295
    // Enable URL language detection and selection.
296
    $edit = array('language_interface[enabled][locale-url]' => 1);
297
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
298 299 300 301 302 303 304
  }

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

307
    $english_node = $this->drupalCreateNode(array('type' => 'page'));
308
    $english_alias = $this->randomName();
309 310

    // Edit the node to set language and path.
311
    $edit = array();
312
    $edit['langcode'] = 'en';
313
    $edit['path[alias]'] = $english_alias;
314
    $this->drupalPost('node/' . $english_node->nid . '/edit', $edit, t('Save'));
315 316

    // Confirm that the alias works.
317
    $this->drupalGet($english_alias);
318
    $this->assertText($english_node->title, 'Alias works.');
319 320 321 322 323

    // Translate the node into French.
    $this->drupalGet('node/' . $english_node->nid . '/translate');
    $this->clickLink(t('add translation'));
    $edit = array();
324
    $langcode = LANGUAGE_NOT_SPECIFIED;
325
    $edit["title"] = $this->randomName();
326
    $edit["body[$langcode][0][value]"] = $this->randomName();
327 328
    $french_alias = $this->randomName();
    $edit['path[alias]'] = $french_alias;
329 330 331 332 333 334
    $this->drupalPost(NULL, $edit, t('Save'));

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

    // Ensure the node was created.
335
    $french_node = $this->drupalGetNodeByTitle($edit["title"]);
336 337 338
    $this->assertTrue(($french_node), 'Node found in database.');

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

342 343
    // Confirm that the alias is returned by url(). Languages are cached on
    // many levels, and we need to clear those caches.
344
    drupal_static_reset('language_list');
345
    drupal_static_reset('locale_url_outbound_alter');
346
    drupal_static_reset('locale_language_url_rewrite_url');
347
    $languages = language_list();
348
    $url = url('node/' . $french_node->nid, array('language' => $languages[$french_node->langcode]));
349
    $this->assertTrue(strpos($url, $edit['path[alias]']), t('URL contains the path alias.'));
350 351 352 353

    // Confirm that the alias works even when changing language negotiation
    // options. Enable User language detection and selection over URL one.
    $edit = array(
354 355 356 357
      'language_interface[enabled][locale-user]' => 1,
      'language_interface[weight][locale-user]' => -9,
      'language_interface[enabled][locale-url]' => 1,
      'language_interface[weight][locale-url]' => -8,
358
    );
359
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
360 361

    // Change user language preference.
362
    $edit = array('preferred_langcode' => 'fr');
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
    $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.
381
    $edit = array('language_interface[enabled][locale-url]' => FALSE);
382
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
383 384 385 386 387 388 389 390 391 392

    // 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);
393
    $this->assertResponse(404, t('Alias for French translation is unavailable when URL language negotiation is disabled.'));
394 395 396 397

    // drupal_lookup_path() has an internal static cache. Check to see that
    // it has the appropriate contents at this point.
    drupal_lookup_path('wipe');
398
    $french_node_path = drupal_lookup_path('source', $french_alias, $french_node->langcode);
399
    $this->assertEqual($french_node_path, 'node/' . $french_node->nid, t('Normal path works.'));
400
    // Second call should return the same path.
401
    $french_node_path = drupal_lookup_path('source', $french_alias, $french_node->langcode);
402
    $this->assertEqual($french_node_path, 'node/' . $french_node->nid, t('Normal path is the same.'));
403 404

    // Confirm that the alias works.
405
    $french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->langcode);
406
    $this->assertEqual($french_node_alias, $french_alias, t('Alias works.'));
407
    // Second call should return the same alias.
408
    $french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->langcode);
409
    $this->assertEqual($french_node_alias, $french_alias, t('Alias is the same.'));
410 411
  }
}
412 413 414 415

/**
 * Tests the user interface for creating path aliases, with languages.
 */
416
class PathLanguageUITestCase extends PathTestCase {
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
  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();
434
    $edit['predefined_langcode'] = 'fr';
435 436 437

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

438
    // Enable URL language detection and selection.
439
    $edit = array('language_interface[enabled][locale-url]' => 1);
440
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
  }

  /**
   * 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;
465
    $edit['langcode'] = 'en';
466 467 468 469 470 471 472 473 474 475 476 477 478 479
    $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;
480
    $edit['langcode'] = 'fr';
481 482 483 484 485 486 487
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));

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

}
488 489 490 491

/**
 * Tests that paths are not prefixed on a monolingual site.
 */
492
class PathMonolingualTestCase extends PathTestCase {
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
  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();
510
    $edit['predefined_langcode'] = 'fr';
511 512 513 514 515 516 517
    $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.
518
    $edit = array('languages[en][enabled]' => FALSE);
519 520 521
    $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));

    // Verify that French is the only language.
522
    $this->assertFalse(language_multilingual(), t('Site is mono-lingual'));
523
    $this->assertEqual(language_default()->langcode, 'fr', t('French is the default language'));
524 525

    // Set language detection to URL.
526
    $edit = array('language_interface[enabled][locale-url]' => TRUE);
527
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548

    // 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');
  }
}