Project

General

Profile

Paste
Download (21.3 KB) Statistics
| Branch: | Revision:

root / drupal7 / modules / path / path.test @ c7768a53

1
<?php
2

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

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

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

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

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

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

    
41
    // Visit the system path for the node and confirm a cache entry is
42
    // created.
43
    cache_clear_all('*', 'cache_path', TRUE);
44
    $this->drupalGet($edit['source']);
45
    $this->assertTrue(cache_get($edit['source'], 'cache_path'), 'Cache entry was created.');
46

    
47
    // Visit the alias for the node and confirm a cache entry is created.
48
    cache_clear_all('*', 'cache_path', TRUE);
49
    $this->drupalGet($edit['alias']);
50
    $this->assertTrue(cache_get($edit['source'], 'cache_path'), 'Cache entry was created.');
51
  }
52

    
53
  /**
54
   * Tests alias functionality through the admin interfaces.
55
   */
56
  function testAdminAlias() {
57
    // Create test node.
58
    $node1 = $this->drupalCreateNode();
59

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

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

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

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

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

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

    
91
    // Create second test node.
92
    $node2 = $this->drupalCreateNode();
93

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

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

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

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

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

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

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

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

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

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

    
146
    // Create second test node.
147
    $node2 = $this->drupalCreateNode();
148

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

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

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

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

    
164
    // Create third test node.
165
    $node3 = $this->drupalCreateNode();
166

    
167
    // Create an invalid alias with a leading slash and verify that the slash
168
    // is removed when the link is generated. This ensures that URL aliases
169
    // cannot be used to inject external URLs.
170
    // @todo The user interface should either display an error message or
171
    //   automatically trim these invalid aliases, rather than allowing them to
172
    //   be silently created, at which point the functional aspects of this
173
    //   test will need to be moved elsewhere and switch to using a
174
    //   programmatically-created alias instead.
175
    $alias = $this->randomName(8);
176
    $edit = array('path[alias]' => '/' . $alias);
177
    $this->drupalPost('node/' . $node3->nid . '/edit', $edit, t('Save'));
178
    $this->drupalGet('admin/content');
179
    // This checks the link href before clicking it, rather than using
180
    // DrupalWebTestCase::assertUrl() after clicking it, because the test
181
    // browser does not always preserve the correct number of slashes in the
182
    // URL when it visits internal links; using DrupalWebTestCase::assertUrl()
183
    // would actually make the test pass unconditionally on the testbot (or
184
    // anywhere else where Drupal is installed in a subdirectory).
185
    $link_xpath = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $node3->title));
186
    $link_href = (string) $link_xpath[0]['href'];
187
    $link_prefix = base_path() . (variable_get('clean_url', 0) ? '' : '?q=');
188
    $this->assertEqual($link_href, $link_prefix . $alias);
189
    $this->clickLink($node3->title);
190
    $this->assertResponse(404);
191
  }
192

    
193
  /**
194
   * Returns the path ID.
195
   *
196
   * @param $alias
197
   *   A string containing an aliased path.
198
   *
199
   * @return int
200
   *   Integer representing the path ID.
201
   */
202
  function getPID($alias) {
203
    return db_query("SELECT pid FROM {url_alias} WHERE alias = :alias", array(':alias' => $alias))->fetchField();
204
  }
205

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

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

    
224
/**
225
 * Tests URL aliases for taxonomy terms.
226
 */
227
class PathTaxonomyTermTestCase extends DrupalWebTestCase {
228
  public static function getInfo() {
229
    return array(
230
      'name' => 'Taxonomy term URL aliases',
231
      'description' => 'Tests URL aliases for taxonomy terms.',
232
      'group' => 'Path',
233
    );
234
  }
235

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

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

    
244
  /**
245
   * Tests alias functionality through the admin interfaces.
246
   */
247
  function testTermAlias() {
248
    // Create a term in the default 'Tags' vocabulary with URL alias.
249
    $vocabulary = taxonomy_vocabulary_load(1);
250
    $description = $this->randomName();;
251
    $edit = array();
252
    $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
    // Confirm that the alias works.
258
    $this->drupalGet($edit['path[alias]']);
259
    $this->assertText($description, 'Term can be accessed on URL alias.');
260

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

    
267
    // Confirm that the changed alias works.
268
    $this->drupalGet($edit2['path[alias]']);
269
    $this->assertText($description, 'Term can be accessed on changed URL alias.');
270

    
271
    // Confirm that the old alias no longer works.
272
    $this->drupalGet($edit['path[alias]']);
273
    $this->assertNoText($description, 'Old URL alias has been removed after altering.');
274
    $this->assertResponse(404, 'Old URL alias returns 404.');
275

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

    
281
    // Confirm that the alias no longer works.
282
    $this->drupalGet($edit2['path[alias]']);
283
    $this->assertNoText($description, 'Old URL alias has been removed after altering.');
284
    $this->assertResponse(404, 'Old URL alias returns 404.');
285
  }
286
}
287

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

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

    
303
    // Create and login user.
304
    $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'));
305
    $this->drupalLogin($this->web_user);
306

    
307
    // Enable French language.
308
    $edit = array();
309
    $edit['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[enabled][locale-url]' => 1);
315
    $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
316
  }
317

    
318
  /**
319
   * Test alias functionality through the admin interfaces.
320
   */
321
  function testAliasTranslation() {
322
    // Set 'page' content type to enable translation.
323
    variable_set('language_content_type_page', 2);
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['language'] = '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
    // Translate the node into French.
339
    $this->drupalGet('node/' . $english_node->nid . '/translate');
340
    $this->clickLink(t('add translation'));
341
    $edit = array();
342
    $langcode = LANGUAGE_NONE;
343
    $edit["title"] = $this->randomName();
344
    $edit["body[$langcode][0][value]"] = $this->randomName();
345
    $french_alias = $this->randomName();
346
    $edit['path[alias]'] = $french_alias;
347
    $this->drupalPost(NULL, $edit, t('Save'));
348

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

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

    
356
    // 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
    // Confirm that the alias is returned by url().
361
    drupal_static_reset('language_list');
362
    drupal_static_reset('locale_url_outbound_alter');
363
    $languages = language_list();
364
    $url = url('node/' . $french_node->nid, array('language' => $languages[$french_node->language]));
365
    $this->assertTrue(strpos($url, $edit['path[alias]']), 'URL contains the path alias.');
366

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

    
377
    // Change user language preference.
378
    $edit = array('language' => 'fr');
379
    $this->drupalPost("user/{$this->web_user->uid}/edit", $edit, t('Save'));
380

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

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

    
396
    // Disable URL language negotiation.
397
    $edit = array('language[enabled][locale-url]' => FALSE);
398
    $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
399

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

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

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

    
420
    // Confirm that the alias works.
421
    $french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->language);
422
    $this->assertEqual($french_node_alias, $french_alias, 'Alias works.');
423
    // Second call should return the same alias.
424
    $french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->language);
425
    $this->assertEqual($french_node_alias, $french_alias, 'Alias is the same.');
426
  }
427
}
428

    
429
/**
430
 * Tests the user interface for creating path aliases, with languages.
431
 */
432
class PathLanguageUITestCase extends DrupalWebTestCase {
433
  public static function getInfo() {
434
    return array(
435
      'name' => 'Path aliases with languages',
436
      'description' => 'Confirm that the Path module user interface works with languages.',
437
      'group' => 'Path',
438
    );
439
  }
440

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

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

    
448
    // Enable French language.
449
    $edit = array();
450
    $edit['langcode'] = 'fr';
451

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

    
454
    // Enable URL language detection and selection.
455
    $edit = array('language[enabled][locale-url]' => 1);
456
    $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
457
  }
458

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

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

    
473
  /**
474
   * Tests that a default language URL alias works.
475
   */
476
  function testDefaultLanguageURLs() {
477
    $name = $this->randomName(8);
478
    $edit = array();
479
    $edit['source'] = 'admin/config/search/path';
480
    $edit['alias'] = $name;
481
    $edit['language'] = 'en';
482
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
483

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

    
488
  /**
489
   * Tests that a non-default language URL alias works.
490
   */
491
  function testNonDefaultURLs() {
492
    $name = $this->randomName(8);
493
    $edit = array();
494
    $edit['source'] = 'admin/config/search/path';
495
    $edit['alias'] = $name;
496
    $edit['language'] = 'fr';
497
    $this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
498

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

    
503
}
504

    
505
/**
506
 * Tests that paths are not prefixed on a monolingual site.
507
 */
508
class PathMonolingualTestCase extends DrupalWebTestCase {
509
  public static function getInfo() {
510
    return array(
511
      'name' => 'Paths on non-English monolingual sites',
512
      'description' => 'Confirm that paths are not changed on monolingual non-English sites',
513
      'group' => 'Path',
514
    );
515
  }
516

    
517
  function setUp() {
518
    global $language;
519
    parent::setUp('path', 'locale', 'translation');
520

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

    
525
    // Enable French language.
526
    $edit = array();
527
    $edit['langcode'] = 'fr';
528
    $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
529

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

    
534
    // Disable English.
535
    $edit = array('enabled[en]' => FALSE);
536
    $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));
537

    
538
    // Verify that French is the only language.
539
    $this->assertFalse(drupal_multilingual(), 'Site is mono-lingual');
540
    $this->assertEqual(language_default('language'), 'fr', 'French is the default language');
541

    
542
    // Set language detection to URL.
543
    $edit = array('language[enabled][locale-url]' => TRUE);
544
    $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
545

    
546
    // Force languages to be initialized.
547
    drupal_language_initialize();
548
  }
549

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

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

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