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

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

8 9 10
/**
 * Provides a base class for testing the Path module.
 */
11
class PathTestCase extends DrupalWebTestCase {
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
  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'));
    }
  }
}

29 30 31
/**
 * Tests path alias functionality.
 */
32
class PathAliasTestCase extends PathTestCase {
33
  public static function getInfo() {
34
    return array(
35 36 37
      'name' => 'Path alias functionality',
      'description' => 'Add, edit, delete, and change alias and verify its consistency in the database.',
      'group' => 'Path',
38 39 40 41 42
    );
  }

  function setUp() {
    parent::setUp('path');
43 44 45

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

49
  /**
50
   * Tests the path cache.
51 52 53 54 55 56 57
   */
  function testPathCache() {
    // Create test node.
    $node1 = $this->drupalCreateNode();

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

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

    // Visit the alias for the node and confirm a cache entry is created.
69
    cache('path')->flush();
70
    $this->drupalGet($edit['alias']);
71
    $this->assertTrue(cache('path')->get($edit['source']), t('Cache entry was created.'));
72 73
  }

74
  /**
75
   * Tests alias functionality through the admin interfaces.
76 77
   */
  function testAdminAlias() {
78
    // Create test node.
79
    $node1 = $this->drupalCreateNode();
80

81
    // Create alias.
82
    $edit = array();
83 84
    $edit['source'] = 'node/' . $node1->nid;
    $edit['alias'] = $this->randomName(8);
85
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
86

87
    // Confirm that the alias works.
88
    $this->drupalGet($edit['alias']);
89
    $this->assertText($node1->title, 'Alias works.');
90
    $this->assertResponse(200);
91

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

95
    $previous = $edit['alias'];
96 97 98
    $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.
99
    $this->drupalPost('admin/config/search/path/edit/' . $pid, $edit, t('Save'));
100

101
    // Confirm that the alias works.
102
    $this->drupalGet($edit['alias']);
103
    $this->assertText($node1->title, 'Changed alias works.');
104
    $this->assertResponse(200);
105

106
    drupal_static_reset('drupal_lookup_path');
107
    // Confirm that previous alias no longer works.
108 109 110 111
    $this->drupalGet($previous);
    $this->assertNoText($node1->title, 'Previous alias no longer works.');
    $this->assertResponse(404);

112
    // Create second test node.
113
    $node2 = $this->drupalCreateNode();
114

115
    // Set alias to second test node.
116 117
    $edit['source'] = 'node/' . $node2->nid;
    // leave $edit['alias'] the same
118
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
119

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

123
    // Delete alias.
124 125
    $this->drupalPost('admin/config/search/path/edit/' . $pid, array(), t('Delete'));
    $this->drupalPost(NULL, array(), t('Confirm'));
126

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

  /**
134
   * Tests alias functionality through the node interfaces.
135 136
   */
  function testNodeAlias() {
137
    // Create test node.
138
    $node1 = $this->drupalCreateNode();
139

140
    // Create alias.
141
    $edit = array();
142
    $edit['path[alias]'] = $this->randomName(8);
143 144
    $this->drupalPost('node/' . $node1->nid . '/edit', $edit, t('Save'));

145
    // Confirm that the alias works.
146
    $this->drupalGet($edit['path[alias]']);
147
    $this->assertText($node1->title, 'Alias works.');
148
    $this->assertResponse(200);
149

150
    // Change alias to one containing "exotic" characters.
151
    $previous = $edit['path[alias]'];
152 153 154
    $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.
155 156
    $this->drupalPost('node/' . $node1->nid . '/edit', $edit, t('Save'));

157
    // Confirm that the alias works.
158
    $this->drupalGet($edit['path[alias]']);
159
    $this->assertText($node1->title, 'Changed alias works.');
160
    $this->assertResponse(200);
161

162
    // Make sure that previous alias no longer works.
163
    $this->drupalGet($previous);
164
    $this->assertNoText($node1->title, 'Previous alias no longer works.');
165 166
    $this->assertResponse(404);

167
    // Create second test node.
168
    $node2 = $this->drupalCreateNode();
169

170
    // Set alias to second test node.
171
    // Leave $edit['path[alias]'] the same.
172 173
    $this->drupalPost('node/' . $node2->nid . '/edit', $edit, t('Save'));

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

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

180
    // Confirm that the alias no longer works.
181
    $this->drupalGet($edit['path[alias]']);
182
    $this->assertNoText($node1->title, 'Alias was successfully deleted.');
183
    $this->assertResponse(404);
184 185
  }

186 187 188 189 190 191 192 193 194
  /**
   * Returns the path ID.
   *
   * @param $alias
   *   A string containing an aliased path.
   *
   * @return int
   *   Integer representing the path ID.
   */
195 196
  function getPID($alias) {
    return db_query("SELECT pid FROM {url_alias} WHERE alias = :alias", array(':alias' => $alias))->fetchField();
197
  }
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

  /**
   * 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.');
  }
215
}
216

217
/**
218
 * Tests URL aliases for taxonomy terms.
219
 */
220
class PathTaxonomyTermTestCase extends PathTestCase {
221 222 223 224 225 226 227 228 229
  public static function getInfo() {
    return array(
      'name' => 'Taxonomy term URL aliases',
      'description' => 'Tests URL aliases for taxonomy terms.',
      'group' => 'Path',
    );
  }

  function setUp() {
230 231 232
    parent::setUp(array('taxonomy'));

    // Create a Tags vocabulary for the Article node type.
233
    $vocabulary = entity_create('taxonomy_vocabulary', array(
234 235
      'name' => t('Tags'),
      'machine_name' => 'tags',
236 237
    ));
    $vocabulary->save();
238 239 240 241 242 243 244

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

  /**
245
   * Tests alias functionality through the admin interfaces.
246 247 248
   */
  function testTermAlias() {
    // Create a term in the default 'Tags' vocabulary with URL alias.
249
    $vocabulary = taxonomy_vocabulary_load(1);
250
    $description = $this->randomName();;
251 252
    $edit = array();
    $edit['name'] = $this->randomName();
253
    $edit['description[value]'] = $description;
254
    $edit['path[alias]'] = $this->randomName();
255
    $this->drupalPost('admin/structure/taxonomy/' . $vocabulary->machine_name . '/add', $edit, t('Save'));
256 257 258

    // Confirm that the alias works.
    $this->drupalGet($edit['path[alias]']);
259
    $this->assertText($description, 'Term can be accessed on URL alias.');
260 261 262 263 264 265 266 267 268

    // 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]']);
269
    $this->assertText($description, 'Term can be accessed on changed URL alias.');
270 271 272

    // Confirm that the old alias no longer works.
    $this->drupalGet($edit['path[alias]']);
273
    $this->assertNoText($description, 'Old URL alias has been removed after altering.');
274 275 276 277 278 279 280 281 282
    $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]']);
283
    $this->assertNoText($description, 'Old URL alias has been removed after altering.');
284 285 286 287
    $this->assertResponse(404, 'Old URL alias returns 404.');
  }
}

288 289 290
/**
 * Tests URL aliases for translated nodes.
 */
291
class PathLanguageTestCase extends PathTestCase {
292
  public static function getInfo() {
293
    return array(
294 295 296
      'name' => 'Path aliases with translated nodes',
      'description' => 'Confirm that paths work with translated nodes',
      'group' => 'Path',
297 298 299 300 301 302 303
    );
  }

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

    // Create and login user.
304 305
    $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);
306 307 308

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

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

313
    // Enable URL language detection and selection.
314
    $edit = array('language_interface[enabled][locale-url]' => 1);
315
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
316 317 318 319 320 321 322
  }

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

325
    $english_node = $this->drupalCreateNode(array('type' => 'page'));
326
    $english_alias = $this->randomName();
327 328

    // Edit the node to set language and path.
329
    $edit = array();
330
    $edit['langcode'] = 'en';
331
    $edit['path[alias]'] = $english_alias;
332
    $this->drupalPost('node/' . $english_node->nid . '/edit', $edit, t('Save'));
333 334

    // Confirm that the alias works.
335
    $this->drupalGet($english_alias);
336
    $this->assertText($english_node->title, 'Alias works.');
337 338 339 340 341

    // Translate the node into French.
    $this->drupalGet('node/' . $english_node->nid . '/translate');
    $this->clickLink(t('add translation'));
    $edit = array();
342
    $langcode = LANGUAGE_NOT_SPECIFIED;
343
    $edit["title"] = $this->randomName();
344
    $edit["body[$langcode][0][value]"] = $this->randomName();
345 346
    $french_alias = $this->randomName();
    $edit['path[alias]'] = $french_alias;
347 348 349 350 351 352
    $this->drupalPost(NULL, $edit, t('Save'));

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

    // Ensure the node was created.
353
    $french_node = $this->drupalGetNodeByTitle($edit["title"]);
354 355 356
    $this->assertTrue(($french_node), 'Node found in database.');

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

360 361
    // Confirm that the alias is returned by url(). Languages are cached on
    // many levels, and we need to clear those caches.
362
    drupal_static_reset('language_list');
363
    drupal_static_reset('locale_url_outbound_alter');
364
    drupal_static_reset('locale_language_url_rewrite_url');
365
    $languages = language_list();
366
    $url = url('node/' . $french_node->nid, array('language' => $languages[$french_node->langcode]));
367
    $this->assertTrue(strpos($url, $edit['path[alias]']), t('URL contains the path alias.'));
368 369 370 371

    // Confirm that the alias works even when changing language negotiation
    // options. Enable User language detection and selection over URL one.
    $edit = array(
372 373 374 375
      'language_interface[enabled][locale-user]' => 1,
      'language_interface[weight][locale-user]' => -9,
      'language_interface[enabled][locale-url]' => 1,
      'language_interface[weight][locale-url]' => -8,
376
    );
377
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
378 379

    // Change user language preference.
380
    $edit = array('preferred_langcode' => 'fr');
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
    $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.
399
    $edit = array('language_interface[enabled][locale-url]' => FALSE);
400
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
401 402 403 404 405 406 407 408 409 410

    // 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);
411
    $this->assertResponse(404, t('Alias for French translation is unavailable when URL language negotiation is disabled.'));
412 413 414 415

    // drupal_lookup_path() has an internal static cache. Check to see that
    // it has the appropriate contents at this point.
    drupal_lookup_path('wipe');
416
    $french_node_path = drupal_lookup_path('source', $french_alias, $french_node->langcode);
417
    $this->assertEqual($french_node_path, 'node/' . $french_node->nid, t('Normal path works.'));
418
    // Second call should return the same path.
419
    $french_node_path = drupal_lookup_path('source', $french_alias, $french_node->langcode);
420
    $this->assertEqual($french_node_path, 'node/' . $french_node->nid, t('Normal path is the same.'));
421 422

    // Confirm that the alias works.
423
    $french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->langcode);
424
    $this->assertEqual($french_node_alias, $french_alias, t('Alias works.'));
425
    // Second call should return the same alias.
426
    $french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->langcode);
427
    $this->assertEqual($french_node_alias, $french_alias, t('Alias is the same.'));
428 429
  }
}
430 431 432 433

/**
 * Tests the user interface for creating path aliases, with languages.
 */
434
class PathLanguageUITestCase extends PathTestCase {
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
  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();
452
    $edit['predefined_langcode'] = 'fr';
453 454 455

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

456
    // Enable URL language detection and selection.
457
    $edit = array('language_interface[enabled][locale-url]' => 1);
458
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
  }

  /**
   * 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;
483
    $edit['langcode'] = 'en';
484 485 486 487 488 489 490 491 492 493 494 495 496 497
    $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;
498
    $edit['langcode'] = 'fr';
499 500 501 502 503 504 505
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));

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

}
506 507 508 509

/**
 * Tests that paths are not prefixed on a monolingual site.
 */
510
class PathMonolingualTestCase extends PathTestCase {
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
  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();
528
    $edit['predefined_langcode'] = 'fr';
529 530 531 532 533 534 535
    $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.
536
    $edit = array('languages[en][enabled]' => FALSE);
537 538 539
    $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));

    // Verify that French is the only language.
540
    $this->assertFalse(language_multilingual(), t('Site is mono-lingual'));
541
    $this->assertEqual(language_default()->langcode, 'fr', t('French is the default language'));
542 543

    // Set language detection to URL.
544
    $edit = array('language_interface[enabled][locale-url]' => TRUE);
545
    $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566

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