Initial Drupal 11 with DDEV setup
This commit is contained in:
@ -0,0 +1,5 @@
|
||||
name: 'Path test miscellaneous utilities'
|
||||
type: module
|
||||
description: 'Utilities for path testing'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\path_test_misc\Hook;
|
||||
|
||||
use Drupal\Core\Hook\Attribute\Hook;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
/**
|
||||
* Hook implementations for path_test_misc.
|
||||
*/
|
||||
class PathTestMiscHooks {
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_presave() for node entities.
|
||||
*
|
||||
* This is invoked from testAliasDuplicationPrevention.
|
||||
*/
|
||||
#[Hook('node_presave')]
|
||||
public function nodePresave(NodeInterface $node): void {
|
||||
if ($node->getTitle() !== 'path duplication test') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the title to be able to check that this code ran.
|
||||
$node->setTitle('path duplication test ran');
|
||||
|
||||
// Create a path alias that has the same values as the one in
|
||||
// PathItem::postSave.
|
||||
$path = \Drupal::entityTypeManager()->getStorage('path_alias')
|
||||
->create([
|
||||
'path' => '/node/1',
|
||||
'alias' => '/my-alias',
|
||||
'langcode' => 'en',
|
||||
]);
|
||||
$path->save();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
name: 'Path test with node grants'
|
||||
type: module
|
||||
description: 'Tests URL alias with hook_node_grants implementation'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\path_test_node_grants\Hook;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Hook\Attribute\Hook;
|
||||
|
||||
/**
|
||||
* Hook implementations for path_test_node_grants.
|
||||
*/
|
||||
class PathTestNodeGrantsHooks {
|
||||
|
||||
/**
|
||||
* Implements hook_node_grants().
|
||||
*/
|
||||
#[Hook('node_grants')]
|
||||
public function nodeGrants(AccountInterface $account, $operation) : array {
|
||||
$grants = [];
|
||||
return $grants;
|
||||
}
|
||||
|
||||
}
|
||||
14
web/core/modules/path/tests/src/Functional/GenericTest.php
Normal file
14
web/core/modules/path/tests/src/Functional/GenericTest.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Functional;
|
||||
|
||||
use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
|
||||
|
||||
/**
|
||||
* Generic module test for path.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
class GenericTest extends GenericModuleTestBase {}
|
||||
132
web/core/modules/path/tests/src/Functional/PathAdminTest.php
Normal file
132
web/core/modules/path/tests/src/Functional/PathAdminTest.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Functional;
|
||||
|
||||
/**
|
||||
* Tests the Path admin UI.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
class PathAdminTest extends PathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['path'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
// Create test user and log in.
|
||||
$web_user = $this->drupalCreateUser([
|
||||
'create page content',
|
||||
'edit own page content',
|
||||
'administer url aliases',
|
||||
'create url aliases',
|
||||
]);
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the filtering aspect of the Path UI.
|
||||
*/
|
||||
public function testPathFiltering(): void {
|
||||
// Create test nodes.
|
||||
$node1 = $this->drupalCreateNode();
|
||||
$node2 = $this->drupalCreateNode();
|
||||
$node3 = $this->drupalCreateNode();
|
||||
|
||||
// Create aliases.
|
||||
$path1 = '/node/' . $node1->id();
|
||||
$alias1 = '/' . $this->randomMachineName(8);
|
||||
$edit = [
|
||||
'path[0][value]' => $path1,
|
||||
'alias[0][value]' => $alias1,
|
||||
];
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
$path2 = '/node/' . $node2->id();
|
||||
$alias2 = '/' . $this->randomMachineName(8);
|
||||
$edit = [
|
||||
'path[0][value]' => $path2,
|
||||
'alias[0][value]' => $alias2,
|
||||
];
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
$path3 = '/node/' . $node3->id();
|
||||
$alias3 = '/' . $this->randomMachineName(4) . '/' . $this->randomMachineName(4);
|
||||
$edit = [
|
||||
'path[0][value]' => $path3,
|
||||
'alias[0][value]' => $alias3,
|
||||
];
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Filter by the first alias.
|
||||
$edit = [
|
||||
'filter' => $alias1,
|
||||
];
|
||||
$this->submitForm($edit, 'Filter');
|
||||
$this->assertSession()->linkByHrefExists($alias1);
|
||||
$this->assertSession()->linkByHrefNotExists($alias2);
|
||||
$this->assertSession()->linkByHrefNotExists($alias3);
|
||||
$this->assertSession()->linkByHrefExists($path1);
|
||||
$this->assertSession()->linkByHrefNotExists($path2);
|
||||
$this->assertSession()->linkByHrefNotExists($path3);
|
||||
|
||||
// Filter by the second alias.
|
||||
$edit = [
|
||||
'filter' => $alias2,
|
||||
];
|
||||
$this->submitForm($edit, 'Filter');
|
||||
$this->assertSession()->linkByHrefNotExists($alias1);
|
||||
$this->assertSession()->linkByHrefExists($alias2);
|
||||
$this->assertSession()->linkByHrefNotExists($alias3);
|
||||
$this->assertSession()->linkByHrefNotExists($path1);
|
||||
$this->assertSession()->linkByHrefExists($path2);
|
||||
$this->assertSession()->linkByHrefNotExists($path3);
|
||||
|
||||
// Filter by the third alias which has a slash.
|
||||
$edit = [
|
||||
'filter' => $alias3,
|
||||
];
|
||||
$this->submitForm($edit, 'Filter');
|
||||
$this->assertSession()->linkByHrefNotExists($alias1);
|
||||
$this->assertSession()->linkByHrefNotExists($alias2);
|
||||
$this->assertSession()->linkByHrefExists($alias3);
|
||||
$this->assertSession()->linkByHrefNotExists($path1);
|
||||
$this->assertSession()->linkByHrefNotExists($path2);
|
||||
$this->assertSession()->linkByHrefExists($path3);
|
||||
|
||||
// Filter by a random string with a different length.
|
||||
$edit = [
|
||||
'filter' => $this->randomMachineName(10),
|
||||
];
|
||||
$this->submitForm($edit, 'Filter');
|
||||
$this->assertSession()->linkByHrefNotExists($alias1);
|
||||
$this->assertSession()->linkByHrefNotExists($alias2);
|
||||
$this->assertSession()->linkByHrefNotExists($path1);
|
||||
$this->assertSession()->linkByHrefNotExists($path2);
|
||||
|
||||
// Reset the filter.
|
||||
$edit = [];
|
||||
$this->submitForm($edit, 'Reset');
|
||||
$this->assertSession()->linkByHrefExists($alias1);
|
||||
$this->assertSession()->linkByHrefExists($alias2);
|
||||
$this->assertSession()->linkByHrefExists($path1);
|
||||
$this->assertSession()->linkByHrefExists($path2);
|
||||
}
|
||||
|
||||
}
|
||||
458
web/core/modules/path/tests/src/Functional/PathAliasTest.php
Normal file
458
web/core/modules/path/tests/src/Functional/PathAliasTest.php
Normal file
@ -0,0 +1,458 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Functional;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\WaitTerminateTestTrait;
|
||||
|
||||
/**
|
||||
* Tests modifying path aliases from the UI.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
class PathAliasTest extends PathTestBase {
|
||||
|
||||
use WaitTerminateTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['path'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
// Create test user and log in.
|
||||
$web_user = $this->drupalCreateUser([
|
||||
'create page content',
|
||||
'edit own page content',
|
||||
'administer url aliases',
|
||||
'create url aliases',
|
||||
'access content overview',
|
||||
]);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// The \Drupal\path_alias\AliasPrefixList service performs cache clears
|
||||
// after Drupal has flushed the response to the client. We use
|
||||
// WaitTerminateTestTrait to wait for Drupal to do this before continuing.
|
||||
$this->setWaitForTerminate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the path cache.
|
||||
*/
|
||||
public function testPathCache(): void {
|
||||
// Create test node.
|
||||
$node1 = $this->drupalCreateNode();
|
||||
|
||||
// Create alias.
|
||||
$edit = [];
|
||||
$edit['path[0][value]'] = '/node/' . $node1->id();
|
||||
$edit['alias[0][value]'] = '/' . $this->randomMachineName(8);
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Check the path alias prefix list cache.
|
||||
$prefix_list = \Drupal::cache('bootstrap')->get('path_alias_prefix_list');
|
||||
$this->assertTrue($prefix_list->data['node']);
|
||||
$this->assertFalse($prefix_list->data['admin']);
|
||||
|
||||
// Visit the system path for the node and confirm a cache entry is
|
||||
// created.
|
||||
\Drupal::cache('data')->deleteAll();
|
||||
// Make sure the path is not converted to the alias.
|
||||
$this->drupalGet(trim($edit['path[0][value]'], '/'), ['alias' => TRUE]);
|
||||
$this->assertNotEmpty(\Drupal::cache('data')->get('preload-paths:' . $edit['path[0][value]']), 'Cache entry was created.');
|
||||
|
||||
// Visit the alias for the node and confirm a cache entry is created.
|
||||
\Drupal::cache('data')->deleteAll();
|
||||
// @todo Remove this once https://www.drupal.org/node/2480077 lands.
|
||||
Cache::invalidateTags(['rendered']);
|
||||
$this->drupalGet(trim($edit['alias[0][value]'], '/'));
|
||||
$this->assertNotEmpty(\Drupal::cache('data')->get('preload-paths:' . $edit['path[0][value]']), 'Cache entry was created.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests alias functionality through the admin interfaces.
|
||||
*/
|
||||
public function testAdminAlias(): void {
|
||||
// Create test node.
|
||||
$node1 = $this->drupalCreateNode();
|
||||
|
||||
// Create alias.
|
||||
$edit = [];
|
||||
$edit['path[0][value]'] = '/node/' . $node1->id();
|
||||
$edit['alias[0][value]'] = '/' . $this->getRandomGenerator()->word(8);
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Confirm that the alias works.
|
||||
$this->drupalGet($edit['alias[0][value]']);
|
||||
$this->assertSession()->pageTextContains($node1->label());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
// Confirm that the alias works in a case-insensitive way.
|
||||
$this->assertTrue(ctype_lower(ltrim($edit['alias[0][value]'], '/')));
|
||||
$this->drupalGet($edit['alias[0][value]']);
|
||||
// Lower case.
|
||||
$this->assertSession()->pageTextContains($node1->label());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->drupalGet(mb_strtoupper($edit['alias[0][value]']));
|
||||
// Upper case.
|
||||
$this->assertSession()->pageTextContains($node1->label());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Change alias to one containing "exotic" characters.
|
||||
$pid = $this->getPID($edit['alias[0][value]']);
|
||||
|
||||
$previous = $edit['alias[0][value]'];
|
||||
// Lower-case letters.
|
||||
$edit['alias[0][value]'] = '/alias' .
|
||||
// "Special" ASCII characters.
|
||||
"- ._~!$'\"()*@[]?&+%#,;=:" .
|
||||
// Characters that look like a percent-escaped string.
|
||||
"%23%25%26%2B%2F%3F" .
|
||||
// Characters from various non-ASCII alphabets.
|
||||
"中國書۞";
|
||||
$connection = Database::getConnection();
|
||||
if ($connection->databaseType() != 'sqlite') {
|
||||
// When using LIKE for case-insensitivity, the SQLite driver is
|
||||
// currently unable to find the upper-case versions of non-ASCII
|
||||
// characters.
|
||||
// @todo fix this in https://www.drupal.org/node/2607432
|
||||
// cSpell:disable-next-line
|
||||
$edit['alias[0][value]'] .= "ïвβéø";
|
||||
}
|
||||
$this->drupalGet('admin/config/search/path/edit/' . $pid);
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Confirm that the alias works.
|
||||
$this->drupalGet(mb_strtoupper($edit['alias[0][value]']));
|
||||
$this->assertSession()->pageTextContains($node1->label());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$this->container->get('path_alias.manager')->cacheClear();
|
||||
// Confirm that previous alias no longer works.
|
||||
$this->drupalGet($previous);
|
||||
$this->assertSession()->pageTextNotContains($node1->label());
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Create second test node.
|
||||
$node2 = $this->drupalCreateNode();
|
||||
|
||||
// Set alias to second test node.
|
||||
$edit['path[0][value]'] = '/node/' . $node2->id();
|
||||
// Leave $edit['alias'] the same
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Confirm no duplicate was created.
|
||||
$this->assertSession()->statusMessageContains("The alias {$edit['alias[0][value]']} is already in use in this language.", 'error');
|
||||
|
||||
$edit_upper = $edit;
|
||||
$edit_upper['alias[0][value]'] = mb_strtoupper($edit['alias[0][value]']);
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit_upper, 'Save');
|
||||
$this->assertSession()->statusMessageContains("The alias {$edit_upper['alias[0][value]']} could not be added because it is already in use in this language with different capitalization: {$edit['alias[0][value]']}.", 'error');
|
||||
|
||||
// Delete alias.
|
||||
$this->drupalGet('admin/config/search/path/edit/' . $pid);
|
||||
$this->clickLink('Delete');
|
||||
$this->assertSession()->pageTextContains("Are you sure you want to delete the URL alias {$edit['alias[0][value]']}?");
|
||||
$this->submitForm([], 'Delete');
|
||||
|
||||
// Confirm that the alias no longer works.
|
||||
$this->drupalGet($edit['alias[0][value]']);
|
||||
$this->assertSession()->pageTextNotContains($node1->label());
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Create a really long alias.
|
||||
$edit = [];
|
||||
$edit['path[0][value]'] = '/node/' . $node1->id();
|
||||
$alias = '/' . $this->randomMachineName(128);
|
||||
$edit['alias[0][value]'] = $alias;
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
// The alias will always be found.
|
||||
$this->assertSession()->pageTextContains($alias);
|
||||
|
||||
// Create third test node.
|
||||
$node3 = $this->drupalCreateNode();
|
||||
|
||||
// Create absolute path alias.
|
||||
$edit = [];
|
||||
$edit['path[0][value]'] = '/node/' . $node3->id();
|
||||
$node3_alias = '/' . $this->randomMachineName(8);
|
||||
$edit['alias[0][value]'] = $node3_alias;
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Create fourth test node.
|
||||
$node4 = $this->drupalCreateNode();
|
||||
|
||||
// Create alias with trailing slash.
|
||||
$edit = [];
|
||||
$edit['path[0][value]'] = '/node/' . $node4->id();
|
||||
$node4_alias = '/' . $this->randomMachineName(8);
|
||||
$edit['alias[0][value]'] = $node4_alias . '/';
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Confirm that the alias with trailing slash is not found.
|
||||
$this->assertSession()->pageTextNotContains($edit['alias[0][value]']);
|
||||
// The alias without trailing flash is found.
|
||||
$this->assertSession()->pageTextContains(trim($edit['alias[0][value]'], '/'));
|
||||
|
||||
// Update an existing alias to point to a different source.
|
||||
$pid = $this->getPID($node4_alias);
|
||||
$edit = [];
|
||||
$edit['alias[0][value]'] = $node4_alias;
|
||||
$edit['path[0][value]'] = '/node/' . $node2->id();
|
||||
$this->drupalGet('admin/config/search/path/edit/' . $pid);
|
||||
$this->submitForm($edit, 'Save');
|
||||
$this->assertSession()->statusMessageContains('The alias has been saved.', 'status');
|
||||
$this->drupalGet($edit['alias[0][value]']);
|
||||
// Previous alias should no longer work.
|
||||
$this->assertSession()->pageTextNotContains($node4->label());
|
||||
// Alias should work.
|
||||
$this->assertSession()->pageTextContains($node2->label());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Update an existing alias to use a duplicate alias.
|
||||
$pid = $this->getPID($node3_alias);
|
||||
$edit = [];
|
||||
$edit['alias[0][value]'] = $node4_alias;
|
||||
$edit['path[0][value]'] = '/node/' . $node3->id();
|
||||
$this->drupalGet('admin/config/search/path/edit/' . $pid);
|
||||
$this->submitForm($edit, 'Save');
|
||||
$this->assertSession()->statusMessageContains("The alias {$edit['alias[0][value]']} is already in use in this language.", 'error');
|
||||
|
||||
// Create an alias without a starting slash.
|
||||
$node5 = $this->drupalCreateNode();
|
||||
|
||||
$edit = [];
|
||||
$edit['path[0][value]'] = 'node/' . $node5->id();
|
||||
$node5_alias = $this->randomMachineName(8);
|
||||
$edit['alias[0][value]'] = $node5_alias . '/';
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
$this->assertSession()->addressEquals('admin/config/search/path/add');
|
||||
$this->assertSession()->statusMessageContains('The source path has to start with a slash.', 'error');
|
||||
$this->assertSession()->statusMessageContains('The alias path has to start with a slash.', 'error');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests alias functionality through the node interfaces.
|
||||
*/
|
||||
public function testNodeAlias(): void {
|
||||
// Create test node.
|
||||
$node1 = $this->drupalCreateNode();
|
||||
|
||||
// Create alias.
|
||||
$edit = [];
|
||||
$edit['path[0][alias]'] = '/' . $this->randomMachineName(8);
|
||||
$this->drupalGet('node/' . $node1->id() . '/edit');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Confirm that the alias works.
|
||||
$this->drupalGet($edit['path[0][alias]']);
|
||||
$this->assertSession()->pageTextContains($node1->label());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Confirm the 'canonical' and 'shortlink' URLs.
|
||||
$this->assertSession()->elementExists('xpath', "//link[contains(@rel, 'canonical') and contains(@href, '" . $edit['path[0][alias]'] . "')]");
|
||||
$this->assertSession()->elementExists('xpath', "//link[contains(@rel, 'shortlink') and contains(@href, 'node/" . $node1->id() . "')]");
|
||||
|
||||
$previous = $edit['path[0][alias]'];
|
||||
// Change alias to one containing "exotic" characters.
|
||||
// Lower-case letters.
|
||||
$edit['path[0][alias]'] = '/alias' .
|
||||
// "Special" ASCII characters.
|
||||
"- ._~!$'\"()*@[]?&+%#,;=:" .
|
||||
// Characters that look like a percent-escaped string.
|
||||
"%23%25%26%2B%2F%3F" .
|
||||
// Characters from various non-ASCII alphabets.
|
||||
"中國書۞";
|
||||
$connection = Database::getConnection();
|
||||
if ($connection->databaseType() != 'sqlite') {
|
||||
// When using LIKE for case-insensitivity, the SQLite driver is
|
||||
// currently unable to find the upper-case versions of non-ASCII
|
||||
// characters.
|
||||
// @todo fix this in https://www.drupal.org/node/2607432
|
||||
// cSpell:disable-next-line
|
||||
$edit['path[0][alias]'] .= "ïвβéø";
|
||||
}
|
||||
$this->drupalGet('node/' . $node1->id() . '/edit');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Confirm that the alias works.
|
||||
$this->drupalGet(mb_strtoupper($edit['path[0][alias]']));
|
||||
$this->assertSession()->pageTextContains($node1->label());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Make sure that previous alias no longer works.
|
||||
$this->drupalGet($previous);
|
||||
$this->assertSession()->pageTextNotContains($node1->label());
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Create second test node.
|
||||
$node2 = $this->drupalCreateNode();
|
||||
|
||||
// Set alias to second test node.
|
||||
// Leave $edit['path[0][alias]'] the same.
|
||||
$this->drupalGet('node/' . $node2->id() . '/edit');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Confirm that the alias didn't make a duplicate.
|
||||
$this->assertSession()->statusMessageContains("The alias {$edit['path[0][alias]']} is already in use in this language.", 'error');
|
||||
|
||||
// Delete alias.
|
||||
$this->drupalGet('node/' . $node1->id() . '/edit');
|
||||
$this->submitForm(['path[0][alias]' => ''], 'Save');
|
||||
|
||||
// Confirm that the alias no longer works.
|
||||
$this->drupalGet($edit['path[0][alias]']);
|
||||
$this->assertSession()->pageTextNotContains($node1->label());
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Create third test node.
|
||||
$node3 = $this->drupalCreateNode();
|
||||
|
||||
// Set its path alias to an absolute path.
|
||||
$edit = ['path[0][alias]' => '/' . $this->randomMachineName(8)];
|
||||
$this->drupalGet('node/' . $node3->id() . '/edit');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Confirm that the alias was converted to a relative path.
|
||||
$this->drupalGet(trim($edit['path[0][alias]'], '/'));
|
||||
$this->assertSession()->pageTextContains($node3->label());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Create fourth test node.
|
||||
$node4 = $this->drupalCreateNode();
|
||||
|
||||
// Set its path alias to have a trailing slash.
|
||||
$edit = ['path[0][alias]' => '/' . $this->randomMachineName(8) . '/'];
|
||||
$this->drupalGet('node/' . $node4->id() . '/edit');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Confirm that the alias was converted to a relative path.
|
||||
$this->drupalGet(trim($edit['path[0][alias]'], '/'));
|
||||
$this->assertSession()->pageTextContains($node4->label());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Create fifth test node.
|
||||
$node5 = $this->drupalCreateNode();
|
||||
|
||||
// Set a path alias.
|
||||
$edit = ['path[0][alias]' => '/' . $this->randomMachineName(8)];
|
||||
$this->drupalGet('node/' . $node5->id() . '/edit');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Delete the node and check that the path alias is also deleted.
|
||||
$node5->delete();
|
||||
$path_alias = \Drupal::service('path_alias.repository')->lookUpBySystemPath('/node/' . $node5->id(), $node5->language()->getId());
|
||||
$this->assertNull($path_alias, 'Alias was successfully deleted when the referenced node was deleted.');
|
||||
|
||||
// Create sixth test node.
|
||||
$node6 = $this->drupalCreateNode();
|
||||
|
||||
// Test the special case where the alias is '0'.
|
||||
$edit = ['path[0][alias]' => '0'];
|
||||
$this->drupalGet($node6->toUrl('edit-form'));
|
||||
$this->submitForm($edit, 'Save');
|
||||
$this->assertSession()->statusMessageContains('The alias path has to start with a slash.', 'error');
|
||||
|
||||
// Create an invalid alias with two leading slashes and verify that the
|
||||
// extra slash is removed when the link is generated. This ensures that URL
|
||||
// aliases cannot be used to inject external URLs.
|
||||
// @todo The user interface should either display an error message or
|
||||
// automatically trim these invalid aliases, rather than allowing them to
|
||||
// be silently created, at which point the functional aspects of this
|
||||
// test will need to be moved elsewhere and switch to using a
|
||||
// programmatically-created alias instead.
|
||||
$alias = $this->randomMachineName(8);
|
||||
$edit = ['path[0][alias]' => '//' . $alias];
|
||||
$this->drupalGet($node6->toUrl('edit-form'));
|
||||
$this->submitForm($edit, 'Save');
|
||||
$this->drupalGet(Url::fromRoute('system.admin_content'));
|
||||
// This checks the link href before clicking it, rather than using
|
||||
// \Drupal\Tests\BrowserTestBase::assertSession()->addressEquals() after
|
||||
// clicking it, because the test browser does not always preserve the
|
||||
// correct number of slashes in the URL when it visits internal links;
|
||||
// using \Drupal\Tests\BrowserTestBase::assertSession()->addressEquals()
|
||||
// would actually make the test pass unconditionally on the testbot (or
|
||||
// anywhere else where Drupal is installed in a subdirectory).
|
||||
$this->assertSession()->elementAttributeContains('xpath', "//a[normalize-space(text())='{$node6->getTitle()}']", 'href', base_path() . $alias);
|
||||
$this->clickLink($node6->getTitle());
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path ID.
|
||||
*
|
||||
* @param string $alias
|
||||
* A string containing an aliased path.
|
||||
*
|
||||
* @return int
|
||||
* Integer representing the path ID.
|
||||
*/
|
||||
public function getPID($alias) {
|
||||
$result = \Drupal::entityTypeManager()->getStorage('path_alias')->getQuery()
|
||||
->condition('alias', $alias, '=')
|
||||
->accessCheck(FALSE)
|
||||
->execute();
|
||||
return reset($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that duplicate aliases fail validation.
|
||||
*/
|
||||
public function testDuplicateNodeAlias(): void {
|
||||
// Create one node with a random alias.
|
||||
$node_one = $this->drupalCreateNode();
|
||||
$edit = [];
|
||||
$edit['path[0][alias]'] = '/' . $this->randomMachineName();
|
||||
$this->drupalGet('node/' . $node_one->id() . '/edit');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Now create another node and try to set the same alias.
|
||||
$node_two = $this->drupalCreateNode();
|
||||
$this->drupalGet('node/' . $node_two->id() . '/edit');
|
||||
$this->submitForm($edit, 'Save');
|
||||
$this->assertSession()->statusMessageContains("The alias {$edit['path[0][alias]']} is already in use in this language.", 'error');
|
||||
$path_alias = $this->assertSession()->fieldExists('path[0][alias]');
|
||||
$this->assertSession()->fieldValueEquals('path[0][alias]', $edit['path[0][alias]']);
|
||||
$this->assertTrue($path_alias->hasClass('error'));
|
||||
|
||||
// Behavior here differs with the inline_form_errors module enabled.
|
||||
// Enable the inline_form_errors module and try this again. This module
|
||||
// improves validation with a link in the error message(s) to the fields
|
||||
// which have invalid input.
|
||||
$this->assertTrue($this->container->get('module_installer')->install(['inline_form_errors'], TRUE), 'Installed inline_form_errors.');
|
||||
// Attempt to edit the second node again, as before.
|
||||
$this->drupalGet('node/' . $node_two->id() . '/edit');
|
||||
$this->submitForm($edit, 'Preview');
|
||||
// This error should still be present next to the field.
|
||||
$this->assertSession()->pageTextContains("The alias {$edit['path[0][alias]']} is already in use in this language.");
|
||||
// The validation error set for the page should include this text.
|
||||
$this->assertSession()->statusMessageContains('1 error has been found: URL alias', 'error');
|
||||
// The text 'URL alias' should be a link.
|
||||
$this->assertSession()->linkExists('URL alias');
|
||||
// The link should be to the ID of the URL alias field.
|
||||
$this->assertSession()->linkByHrefExists('#edit-path-0-alias');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
|
||||
use Drupal\Tests\content_translation\Traits\ContentTranslationTestTrait;
|
||||
|
||||
/**
|
||||
* Tests path aliases with Content Moderation.
|
||||
*
|
||||
* @group content_moderation
|
||||
* @group path
|
||||
*/
|
||||
class PathContentModerationTest extends BrowserTestBase {
|
||||
|
||||
use ContentModerationTestTrait;
|
||||
use ContentTranslationTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'node',
|
||||
'path',
|
||||
'content_moderation',
|
||||
'content_translation',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
static::createLanguageFromLangcode('fr');
|
||||
$this->rebuildContainer();
|
||||
|
||||
// Created a content type.
|
||||
$this->drupalCreateContentType([
|
||||
'name' => 'moderated',
|
||||
'type' => 'moderated',
|
||||
]);
|
||||
|
||||
// Set the content type as moderated.
|
||||
$workflow = $this->createEditorialWorkflow();
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'moderated');
|
||||
$workflow->save();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'administer workflows',
|
||||
'access administration pages',
|
||||
'administer content types',
|
||||
'administer content translation',
|
||||
'administer nodes',
|
||||
'view latest version',
|
||||
'view any unpublished content',
|
||||
'access content overview',
|
||||
'use editorial transition create_new_draft',
|
||||
'use editorial transition publish',
|
||||
'use editorial transition archive',
|
||||
'use editorial transition archived_draft',
|
||||
'use editorial transition archived_published',
|
||||
'administer languages',
|
||||
'administer site configuration',
|
||||
'administer url aliases',
|
||||
'create url aliases',
|
||||
'view the administration theme',
|
||||
'translate any entity',
|
||||
'create content translations',
|
||||
'create moderated content',
|
||||
'edit own moderated content',
|
||||
]));
|
||||
|
||||
// Enable URL language detection and selection.
|
||||
$edit = ['language_interface[enabled][language-url]' => 1];
|
||||
$this->drupalGet('admin/config/regional/language/detection');
|
||||
$this->submitForm($edit, 'Save settings');
|
||||
|
||||
// Enable translation for page.
|
||||
$this->enableContentTranslation('node', 'moderated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests node path aliases on a moderated content type.
|
||||
*/
|
||||
public function testNodePathAlias(): void {
|
||||
// Create some moderated content with a path alias.
|
||||
$this->drupalGet('node/add/moderated');
|
||||
$this->assertSession()->fieldValueEquals('path[0][alias]', '');
|
||||
$this->submitForm([
|
||||
'title[0][value]' => 'moderated content',
|
||||
'path[0][alias]' => '/moderated-content',
|
||||
'moderation_state[0][state]' => 'published',
|
||||
], 'Save');
|
||||
$node = $this->getNodeByTitle('moderated content');
|
||||
|
||||
// Add a pending revision with the same alias.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertSession()->fieldValueEquals('path[0][alias]', '/moderated-content');
|
||||
$this->submitForm([
|
||||
'title[0][value]' => 'pending revision',
|
||||
'path[0][alias]' => '/moderated-content',
|
||||
'moderation_state[0][state]' => 'draft',
|
||||
], 'Save');
|
||||
$this->assertSession()->statusMessageNotContains('You can only change the URL alias for the published version of this content.');
|
||||
|
||||
// Create some moderated content with no path alias.
|
||||
$this->drupalGet('node/add/moderated');
|
||||
$this->assertSession()->fieldValueEquals('path[0][alias]', '');
|
||||
$this->submitForm([
|
||||
'title[0][value]' => 'moderated content 2',
|
||||
'path[0][alias]' => '',
|
||||
'moderation_state[0][state]' => 'published',
|
||||
], 'Save');
|
||||
$node = $this->getNodeByTitle('moderated content 2');
|
||||
|
||||
// Add a pending revision with a new alias.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertSession()->fieldValueEquals('path[0][alias]', '');
|
||||
$this->submitForm([
|
||||
'title[0][value]' => 'pending revision',
|
||||
'path[0][alias]' => '/pending-revision',
|
||||
'moderation_state[0][state]' => 'draft',
|
||||
], 'Save');
|
||||
$this->assertSession()->statusMessageContains('You can only change the URL alias for the published version of this content.', 'error');
|
||||
|
||||
// Create some moderated content with no path alias.
|
||||
$this->drupalGet('node/add/moderated');
|
||||
$this->assertSession()->fieldValueEquals('path[0][alias]', '');
|
||||
$this->submitForm([
|
||||
'title[0][value]' => 'moderated content 3',
|
||||
'path[0][alias]' => '',
|
||||
'moderation_state[0][state]' => 'published',
|
||||
], 'Save');
|
||||
$node = $this->getNodeByTitle('moderated content 3');
|
||||
|
||||
// Add a pending revision with no path alias.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertSession()->fieldValueEquals('path[0][alias]', '');
|
||||
$this->submitForm([
|
||||
'title[0][value]' => 'pending revision',
|
||||
'path[0][alias]' => '',
|
||||
'moderation_state[0][state]' => 'draft',
|
||||
], 'Save');
|
||||
$this->assertSession()->statusMessageNotContains('You can only change the URL alias for the published version of this content.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that translated and moderated node can get new draft revision.
|
||||
*/
|
||||
public function testTranslatedModeratedNodeAlias(): void {
|
||||
// Create one node with a random alias.
|
||||
$default_node = $this->drupalCreateNode([
|
||||
'type' => 'moderated',
|
||||
'langcode' => 'en',
|
||||
'moderation_state' => 'published',
|
||||
'path' => '/' . $this->randomMachineName(),
|
||||
]);
|
||||
|
||||
// Add published translation with another alias.
|
||||
$this->drupalGet('node/' . $default_node->id());
|
||||
$this->drupalGet('node/' . $default_node->id() . '/translations');
|
||||
$this->clickLink('Add');
|
||||
$edit_translation = [
|
||||
'body[0][value]' => $this->randomMachineName(),
|
||||
'moderation_state[0][state]' => 'published',
|
||||
'path[0][alias]' => '/' . $this->randomMachineName(),
|
||||
];
|
||||
$this->submitForm($edit_translation, 'Save (this translation)');
|
||||
// Confirm that the alias works.
|
||||
$this->drupalGet('fr' . $edit_translation['path[0][alias]']);
|
||||
$this->assertSession()->pageTextContains($edit_translation['body[0][value]']);
|
||||
|
||||
$default_path = $default_node->path->alias;
|
||||
$translation_path = 'fr' . $edit_translation['path[0][alias]'];
|
||||
|
||||
$this->assertPathsAreAccessible([$default_path, $translation_path]);
|
||||
|
||||
// Try to create new draft revision for translation with a new path alias.
|
||||
$edit_new_translation_draft_with_alias = [
|
||||
'moderation_state[0][state]' => 'draft',
|
||||
'path[0][alias]' => '/' . $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalGet('fr/node/' . $default_node->id() . '/edit');
|
||||
$this->submitForm($edit_new_translation_draft_with_alias, 'Save (this translation)');
|
||||
// Confirm the expected error.
|
||||
$this->assertSession()->statusMessageContains('You can only change the URL alias for the published version of this content.', 'error');
|
||||
|
||||
// Create new draft revision for translation without changing path alias.
|
||||
$edit_new_translation_draft = [
|
||||
'body[0][value]' => $this->randomMachineName(),
|
||||
'moderation_state[0][state]' => 'draft',
|
||||
];
|
||||
$this->drupalGet('fr/node/' . $default_node->id() . '/edit');
|
||||
$this->submitForm($edit_new_translation_draft, 'Save (this translation)');
|
||||
// Confirm that the new draft revision was created.
|
||||
$this->assertSession()->statusMessageNotContains('You can only change the URL alias for the published version of this content.');
|
||||
$this->assertSession()->pageTextContains($edit_new_translation_draft['body[0][value]']);
|
||||
$this->assertPathsAreAccessible([$default_path, $translation_path]);
|
||||
|
||||
// Try to create a new draft revision for translation with path alias from
|
||||
// the original language's default revision.
|
||||
$edit_new_translation_draft_with_defaults_alias = [
|
||||
'moderation_state[0][state]' => 'draft',
|
||||
'path[0][alias]' => $default_node->path->alias,
|
||||
];
|
||||
$this->drupalGet('fr/node/' . $default_node->id() . '/edit');
|
||||
$this->submitForm($edit_new_translation_draft_with_defaults_alias, 'Save (this translation)');
|
||||
// Verify the expected error.
|
||||
$this->assertSession()->statusMessageContains('You can only change the URL alias for the published version of this content.', 'error');
|
||||
|
||||
// Try to create new draft revision for translation with deleted (empty)
|
||||
// path alias.
|
||||
$edit_new_translation_draft_empty_alias = [
|
||||
'body[0][value]' => $this->randomMachineName(),
|
||||
'moderation_state[0][state]' => 'draft',
|
||||
'path[0][alias]' => '',
|
||||
];
|
||||
$this->drupalGet('fr/node/' . $default_node->id() . '/edit');
|
||||
$this->submitForm($edit_new_translation_draft_empty_alias, 'Save (this translation)');
|
||||
// Confirm the expected error.
|
||||
$this->assertSession()->statusMessageContains('You can only change the URL alias for the published version of this content.', 'error');
|
||||
|
||||
// Create new default (published) revision for translation with new path
|
||||
// alias.
|
||||
$edit_new_translation = [
|
||||
'body[0][value]' => $this->randomMachineName(),
|
||||
'moderation_state[0][state]' => 'published',
|
||||
'path[0][alias]' => '/' . $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalGet('fr/node/' . $default_node->id() . '/edit');
|
||||
$this->submitForm($edit_new_translation, 'Save (this translation)');
|
||||
// Confirm that the new published revision was created.
|
||||
$this->assertSession()->statusMessageNotContains('You can only change the URL alias for the published version of this content.');
|
||||
$this->assertSession()->pageTextContains($edit_new_translation['body[0][value]']);
|
||||
$this->assertSession()->addressEquals('fr' . $edit_new_translation['path[0][alias]']);
|
||||
$this->assertPathsAreAccessible([$default_path]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper callback to verify paths are responding with status 200.
|
||||
*
|
||||
* @param string[] $paths
|
||||
* An array of paths to check for.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function assertPathsAreAccessible(array $paths): void {
|
||||
foreach ($paths as $path) {
|
||||
$this->drupalGet($path);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
201
web/core/modules/path/tests/src/Functional/PathLanguageTest.php
Normal file
201
web/core/modules/path/tests/src/Functional/PathLanguageTest.php
Normal file
@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Functional;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUser;
|
||||
use Drupal\Tests\content_translation\Traits\ContentTranslationTestTrait;
|
||||
|
||||
/**
|
||||
* Confirm that paths work with translated nodes.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
class PathLanguageTest extends PathTestBase {
|
||||
|
||||
use ContentTranslationTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'path',
|
||||
'locale',
|
||||
'locale_test',
|
||||
'content_translation',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* A user with permissions to administer content types.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$permissions = [
|
||||
'access administration pages',
|
||||
'administer content translation',
|
||||
'administer content types',
|
||||
'administer languages',
|
||||
'administer url aliases',
|
||||
'create content translations',
|
||||
'create page content',
|
||||
'create url aliases',
|
||||
'edit any page content',
|
||||
'translate any entity',
|
||||
];
|
||||
// Create and log in user.
|
||||
$this->webUser = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// Enable French language.
|
||||
static::createLanguageFromLangcode('fr');
|
||||
|
||||
// Enable URL language detection and selection.
|
||||
$this->container->get('language_negotiator')->saveConfiguration(LanguageInterface::TYPE_URL, [LanguageNegotiationUrl::METHOD_ID => 1]);
|
||||
|
||||
// Enable translation for page node.
|
||||
static::enableContentTranslation('node', 'page');
|
||||
static::setFieldTranslatable('node', 'page', 'body', TRUE);
|
||||
|
||||
$definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('node', 'page');
|
||||
$this->assertTrue($definitions['path']->isTranslatable(), 'Node path is translatable.');
|
||||
$this->assertTrue($definitions['body']->isTranslatable(), 'Node body is translatable.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests alias functionality through the admin interfaces.
|
||||
*/
|
||||
public function testAliasTranslation(): void {
|
||||
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
|
||||
$english_node = $this->drupalCreateNode(['type' => 'page', 'langcode' => 'en']);
|
||||
$english_alias = $this->randomMachineName();
|
||||
|
||||
// Edit the node to set language and path.
|
||||
$edit = [];
|
||||
$edit['path[0][alias]'] = '/' . $english_alias;
|
||||
$this->drupalGet('node/' . $english_node->id() . '/edit');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Confirm that the alias works.
|
||||
$this->drupalGet($english_alias);
|
||||
$this->assertSession()->pageTextContains($english_node->body->value);
|
||||
|
||||
// Translate the node into French.
|
||||
$this->drupalGet('node/' . $english_node->id() . '/translations');
|
||||
$this->clickLink('Add');
|
||||
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = $this->randomMachineName();
|
||||
$edit['body[0][value]'] = $this->randomMachineName();
|
||||
$french_alias = $this->randomMachineName();
|
||||
$edit['path[0][alias]'] = '/' . $french_alias;
|
||||
$this->submitForm($edit, 'Save (this translation)');
|
||||
|
||||
// Clear the path lookup cache.
|
||||
$this->container->get('path_alias.manager')->cacheClear();
|
||||
|
||||
// Languages are cached on many levels, and we need to clear those caches.
|
||||
$this->container->get('language_manager')->reset();
|
||||
$this->rebuildContainer();
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
|
||||
// Ensure the node was created.
|
||||
$english_node = $node_storage->load($english_node->id());
|
||||
$english_node_french_translation = $english_node->getTranslation('fr');
|
||||
$this->assertTrue($english_node->hasTranslation('fr'), 'Node found in database.');
|
||||
|
||||
// Confirm that the alias works.
|
||||
$this->drupalGet('fr' . $edit['path[0][alias]']);
|
||||
$this->assertSession()->pageTextContains($english_node_french_translation->body->value);
|
||||
|
||||
// Confirm that the alias is returned for the URL. Languages are cached on
|
||||
// many levels, and we need to clear those caches.
|
||||
$this->container->get('language_manager')->reset();
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
$url = $english_node_french_translation->toUrl('canonical', ['language' => $languages['fr']])->toString();
|
||||
|
||||
$this->assertStringContainsString($edit['path[0][alias]'], $url, 'URL contains the path alias.');
|
||||
|
||||
// Confirm that the alias works even when changing language negotiation
|
||||
// options. Enable User language detection and selection over URL one.
|
||||
$this->container->get('language_negotiator')->saveConfiguration(LanguageInterface::TYPE_INTERFACE, [LanguageNegotiationUser::METHOD_ID => 1]);
|
||||
$this->container->get('language_negotiator')->saveConfiguration(LanguageInterface::TYPE_URL, [LanguageNegotiationUrl::METHOD_ID => 1]);
|
||||
|
||||
// Change user language preference.
|
||||
$user = User::load($this->webUser->id());
|
||||
$user->set('preferred_langcode', 'fr');
|
||||
$user->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 the alias manager
|
||||
// needs to use the URL language to check whether the alias is valid.
|
||||
$this->drupalGet($english_alias);
|
||||
$this->assertSession()->pageTextContains($english_node_french_translation->body->value);
|
||||
|
||||
// Check that the French alias works.
|
||||
$this->drupalGet("fr/$french_alias");
|
||||
$this->assertSession()->pageTextContains($english_node_french_translation->body->value);
|
||||
|
||||
// Disable URL language negotiation.
|
||||
$this->container->get('language_negotiator')->saveConfiguration(LanguageInterface::TYPE_URL, [LanguageNegotiationUrl::METHOD_ID => FALSE]);
|
||||
|
||||
// Check that the English alias still works.
|
||||
$this->drupalGet($english_alias);
|
||||
$this->assertSession()->pageTextContains($english_node_french_translation->body->value);
|
||||
|
||||
// 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);
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// The alias manager has an internal path lookup cache. Check to see that
|
||||
// it has the appropriate contents at this point.
|
||||
$this->container->get('path_alias.manager')->cacheClear();
|
||||
$french_node_path = $this->container->get('path_alias.manager')->getPathByAlias('/' . $french_alias, 'fr');
|
||||
$this->assertEquals('/node/' . $english_node_french_translation->id(), $french_node_path, 'Normal path works.');
|
||||
// Second call should return the same path.
|
||||
$french_node_path = $this->container->get('path_alias.manager')->getPathByAlias('/' . $french_alias, 'fr');
|
||||
$this->assertEquals('/node/' . $english_node_french_translation->id(), $french_node_path, 'Normal path is the same.');
|
||||
|
||||
// Confirm that the alias works.
|
||||
$french_node_alias = $this->container->get('path_alias.manager')->getAliasByPath('/node/' . $english_node_french_translation->id(), 'fr');
|
||||
$this->assertEquals('/' . $french_alias, $french_node_alias, 'Alias works.');
|
||||
// Second call should return the same alias.
|
||||
$french_node_alias = $this->container->get('path_alias.manager')->getAliasByPath('/node/' . $english_node_french_translation->id(), 'fr');
|
||||
$this->assertEquals('/' . $french_alias, $french_node_alias, 'Alias is the same.');
|
||||
|
||||
// Confirm that the alias is removed if the translation is deleted.
|
||||
$english_node->removeTranslation('fr');
|
||||
$english_node->save();
|
||||
$this->assertPathAliasNotExists('/' . $french_alias, 'fr', NULL, 'Alias for French translation is removed when translation is deleted.');
|
||||
|
||||
// Check that the English alias still works.
|
||||
$this->drupalGet($english_alias);
|
||||
$this->assertPathAliasExists('/' . $english_alias, 'en', NULL, 'English alias is not deleted when French translation is removed.');
|
||||
$this->assertSession()->pageTextContains($english_node->body->value);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Functional;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
* Confirm that the Path module user interface works with languages.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
class PathLanguageUiTest extends PathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['path', 'locale', 'locale_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
// Create and log in user.
|
||||
$web_user = $this->drupalCreateUser([
|
||||
'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 = [];
|
||||
$edit['predefined_langcode'] = 'fr';
|
||||
|
||||
$this->drupalGet('admin/config/regional/language/add');
|
||||
$this->submitForm($edit, 'Add language');
|
||||
|
||||
// Enable URL language detection and selection.
|
||||
$edit = ['language_interface[enabled][language-url]' => 1];
|
||||
$this->drupalGet('admin/config/regional/language/detection');
|
||||
$this->submitForm($edit, 'Save settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a language-neutral URL alias works.
|
||||
*/
|
||||
public function testLanguageNeutralUrl(): void {
|
||||
$name = $this->randomMachineName(8);
|
||||
$edit = [];
|
||||
$edit['path[0][value]'] = '/admin/config/search/path';
|
||||
$edit['alias[0][value]'] = '/' . $name;
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
$this->drupalGet($name);
|
||||
$this->assertSession()->pageTextContains('Filter aliases');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a default language URL alias works.
|
||||
*/
|
||||
public function testDefaultLanguageUrl(): void {
|
||||
$name = $this->randomMachineName(8);
|
||||
$edit = [];
|
||||
$edit['path[0][value]'] = '/admin/config/search/path';
|
||||
$edit['alias[0][value]'] = '/' . $name;
|
||||
$edit['langcode[0][value]'] = 'en';
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
$this->drupalGet($name);
|
||||
$this->assertSession()->pageTextContains('Filter aliases');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a non-default language URL alias works.
|
||||
*/
|
||||
public function testNonDefaultUrl(): void {
|
||||
$name = $this->randomMachineName(8);
|
||||
$edit = [];
|
||||
$edit['path[0][value]'] = '/admin/config/search/path';
|
||||
$edit['alias[0][value]'] = '/' . $name;
|
||||
$edit['langcode[0][value]'] = 'fr';
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
$this->drupalGet('fr/' . $name);
|
||||
$this->assertSession()->pageTextContains('Filter aliases');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests language unspecific aliases are shown and saved in the node form.
|
||||
*/
|
||||
public function testNotSpecifiedNode(): void {
|
||||
// Create test node.
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
// Create a language-unspecific alias in the admin UI, ensure that is
|
||||
// displayed and the langcode is not changed when saving.
|
||||
$edit = [
|
||||
'path[0][value]' => '/node/' . $node->id(),
|
||||
'alias[0][value]' => '/' . $this->getRandomGenerator()->word(8),
|
||||
'langcode[0][value]' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
];
|
||||
$this->drupalGet('admin/config/search/path/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
$this->drupalGet($node->toUrl('edit-form'));
|
||||
$this->assertSession()->fieldValueEquals('path[0][alias]', $edit['alias[0][value]']);
|
||||
$this->submitForm([], 'Save');
|
||||
|
||||
$this->drupalGet('admin/config/search/path');
|
||||
$this->assertSession()->pageTextContains('None');
|
||||
$this->assertSession()->pageTextNotContains('English');
|
||||
|
||||
// Create another node, with no alias, to ensure non-language specific
|
||||
// aliases are loaded correctly.
|
||||
$node = $this->drupalCreateNode();
|
||||
$this->drupalGet($node->toUrl('edit-form'));
|
||||
$this->submitForm([], 'Save');
|
||||
$this->assertSession()->pageTextNotContains('The alias is already in use.');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Functional;
|
||||
|
||||
use Drupal\media\Entity\MediaType;
|
||||
|
||||
/**
|
||||
* Tests the path media form UI.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
class PathMediaFormTest extends PathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['media', 'media_test_source'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
// Create test user and log in.
|
||||
$web_user = $this->drupalCreateUser(['create media', 'create url aliases']);
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the media form UI.
|
||||
*/
|
||||
public function testMediaForm(): void {
|
||||
$assert_session = $this->assertSession();
|
||||
|
||||
// Create media type.
|
||||
$media_type_id = 'foo';
|
||||
$media_type = MediaType::create([
|
||||
'id' => $media_type_id,
|
||||
'label' => $media_type_id,
|
||||
'source' => 'test',
|
||||
'source_configuration' => [],
|
||||
'field_map' => [],
|
||||
'new_revision' => FALSE,
|
||||
]);
|
||||
$media_type->save();
|
||||
|
||||
$this->drupalGet('media/add/' . $media_type_id);
|
||||
|
||||
// Make sure we have a vertical tab fieldset and 'Path' field.
|
||||
$assert_session->elementContains('css', '.js-form-type-vertical-tabs #edit-path-0 summary', 'URL alias');
|
||||
$assert_session->fieldExists('path[0][alias]');
|
||||
|
||||
// Disable the 'Path' field for this content type.
|
||||
\Drupal::service('entity_display.repository')->getFormDisplay('media', $media_type_id, 'default')
|
||||
->removeComponent('path')
|
||||
->save();
|
||||
|
||||
$this->drupalGet('media/add/' . $media_type_id);
|
||||
|
||||
// See if the whole fieldset is gone now.
|
||||
$assert_session->elementNotExists('css', '.js-form-type-vertical-tabs #edit-path-0');
|
||||
$assert_session->fieldNotExists('path[0][alias]');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Functional;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
/**
|
||||
* Tests the Path Node form UI.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
class PathNodeFormTest extends PathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['node', 'path', 'path_test_misc'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
// Create test user and log in.
|
||||
$web_user = $this->drupalCreateUser([
|
||||
'create page content',
|
||||
'create url aliases',
|
||||
]);
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the node form ui.
|
||||
*/
|
||||
public function testNodeForm(): void {
|
||||
$assert_session = $this->assertSession();
|
||||
|
||||
$this->drupalGet('node/add/page');
|
||||
|
||||
// Make sure we have a vertical tab fieldset and 'Path' fields.
|
||||
$assert_session->elementContains('css', '.js-form-type-vertical-tabs #edit-path-0 summary', 'URL alias');
|
||||
$assert_session->fieldExists('path[0][alias]');
|
||||
|
||||
// Disable the 'Path' field for this content type.
|
||||
\Drupal::service('entity_display.repository')->getFormDisplay('node', 'page', 'default')
|
||||
->removeComponent('path')
|
||||
->save();
|
||||
|
||||
$this->drupalGet('node/add/page');
|
||||
|
||||
// See if the whole fieldset is gone now.
|
||||
$assert_session->elementNotExists('css', '.js-form-type-vertical-tabs #edit-path-0');
|
||||
$assert_session->fieldNotExists('path[0][alias]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that duplicate path aliases don't get created.
|
||||
*/
|
||||
public function testAliasDuplicationPrevention(): void {
|
||||
$this->drupalGet('node/add/page');
|
||||
$edit['title[0][value]'] = 'path duplication test';
|
||||
$edit['path[0][alias]'] = '/my-alias';
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Test that PathItem::postSave detects if a path alias exists
|
||||
// before creating one.
|
||||
$aliases = \Drupal::entityTypeManager()
|
||||
->getStorage('path_alias')
|
||||
->loadMultiple();
|
||||
static::assertCount(1, $aliases);
|
||||
$node = Node::load(1);
|
||||
static::assertInstanceOf(NodeInterface::class, $node);
|
||||
|
||||
// This updated title gets set in PathTestMiscHooks::nodePresave. This
|
||||
// is a way of ensuring that bit of test code runs.
|
||||
static::assertEquals('path duplication test ran', $node->getTitle());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Functional;
|
||||
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Tests URL aliases for taxonomy terms.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
class PathTaxonomyTermTest extends PathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['taxonomy'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
// Create a Tags vocabulary for the Article node type.
|
||||
$vocabulary = Vocabulary::create([
|
||||
'name' => 'Tags',
|
||||
'vid' => 'tags',
|
||||
]);
|
||||
$vocabulary->save();
|
||||
|
||||
// Create and log in user.
|
||||
$web_user = $this->drupalCreateUser([
|
||||
'administer url aliases',
|
||||
'administer taxonomy',
|
||||
'access administration pages',
|
||||
]);
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests alias functionality through the admin interfaces.
|
||||
*/
|
||||
public function testTermAlias(): void {
|
||||
// Create a term in the default 'Tags' vocabulary with URL alias.
|
||||
$vocabulary = Vocabulary::load('tags');
|
||||
$description = $this->randomMachineName();
|
||||
$edit = [
|
||||
'name[0][value]' => $this->randomMachineName(),
|
||||
'description[0][value]' => $description,
|
||||
'path[0][alias]' => '/' . $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
$tids = \Drupal::entityQuery('taxonomy_term')
|
||||
->accessCheck(FALSE)
|
||||
->condition('name', $edit['name[0][value]'])
|
||||
->condition('default_langcode', 1)
|
||||
->execute();
|
||||
$tid = reset($tids);
|
||||
|
||||
// Confirm that the alias works.
|
||||
$this->drupalGet($edit['path[0][alias]']);
|
||||
$this->assertSession()->pageTextContains($description);
|
||||
|
||||
// Confirm the 'canonical' and 'shortlink' URLs.
|
||||
$this->assertSession()->elementExists('xpath', "//link[contains(@rel, 'canonical') and contains(@href, '" . $edit['path[0][alias]'] . "')]");
|
||||
$this->assertSession()->elementExists('xpath', "//link[contains(@rel, 'shortlink') and contains(@href, 'taxonomy/term/" . $tid . "')]");
|
||||
|
||||
// Change the term's URL alias.
|
||||
$edit2 = [];
|
||||
$edit2['path[0][alias]'] = '/' . $this->randomMachineName();
|
||||
$this->drupalGet('taxonomy/term/' . $tid . '/edit');
|
||||
$this->submitForm($edit2, 'Save');
|
||||
|
||||
// Confirm that the changed alias works.
|
||||
$this->drupalGet(trim($edit2['path[0][alias]'], '/'));
|
||||
$this->assertSession()->pageTextContains($description);
|
||||
|
||||
// Confirm that the old alias no longer works.
|
||||
$this->drupalGet(trim($edit['path[0][alias]'], '/'));
|
||||
$this->assertSession()->pageTextNotContains($description);
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Remove the term's URL alias.
|
||||
$edit3 = [];
|
||||
$edit3['path[0][alias]'] = '';
|
||||
$this->drupalGet('taxonomy/term/' . $tid . '/edit');
|
||||
$this->submitForm($edit3, 'Save');
|
||||
|
||||
// Confirm that the alias no longer works.
|
||||
$this->drupalGet(trim($edit2['path[0][alias]'], '/'));
|
||||
$this->assertSession()->pageTextNotContains($description);
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
}
|
||||
|
||||
}
|
||||
35
web/core/modules/path/tests/src/Functional/PathTestBase.php
Normal file
35
web/core/modules/path/tests/src/Functional/PathTestBase.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\Traits\Core\PathAliasTestTrait;
|
||||
|
||||
/**
|
||||
* Provides a base class for testing the Path module.
|
||||
*/
|
||||
abstract class PathTestBase extends BrowserTestBase {
|
||||
|
||||
use PathAliasTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['node', 'path'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
// Create Basic page and Article node types.
|
||||
if ($this->profile != 'standard') {
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
|
||||
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Functional;
|
||||
|
||||
use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
|
||||
use Drupal\Tests\content_translation\Traits\ContentTranslationTestTrait;
|
||||
|
||||
/**
|
||||
* Confirm that paths work with node access grants implementations.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
class PathWithNodeAccessGrantsTest extends PathTestBase {
|
||||
|
||||
use ContentTranslationTestTrait;
|
||||
use ContentModerationTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'path',
|
||||
'locale',
|
||||
'locale_test',
|
||||
'content_translation',
|
||||
'content_moderation',
|
||||
'path_test_node_grants',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
// Create a workflow for basic page.
|
||||
$workflow = $this->createEditorialWorkflow();
|
||||
$this->addEntityTypeAndBundleToWorkflow($workflow, 'node', 'page');
|
||||
|
||||
// Login as admin user to configure language detection and selection.
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'edit any page content',
|
||||
'create page content',
|
||||
'administer url aliases',
|
||||
'create url aliases',
|
||||
'administer languages',
|
||||
'access administration pages',
|
||||
]);
|
||||
$this->drupalLogin($admin_user);
|
||||
// Enable French language.
|
||||
static::createLanguageFromLangcode('fr');
|
||||
// Enable URL language detection and selection.
|
||||
$edit = ['language_interface[enabled][language-url]' => 1];
|
||||
$this->drupalGet('admin/config/regional/language/detection');
|
||||
$this->submitForm($edit, 'Save settings');
|
||||
// Enable translation for page node.
|
||||
static::enableContentTranslation('node', 'page');
|
||||
static::setFieldTranslatable('node', 'page', 'body', TRUE);
|
||||
|
||||
$definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('node', 'page');
|
||||
$this->assertTrue($definitions['path']->isTranslatable(), 'Node path is translatable.');
|
||||
$this->assertTrue($definitions['body']->isTranslatable(), 'Node body is translatable.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests alias functionality through the admin interfaces.
|
||||
*/
|
||||
public function testAliasTranslation() : void {
|
||||
// Rebuild the permissions to update 'node_access' table.
|
||||
node_access_rebuild();
|
||||
$alias = $this->randomMachineName();
|
||||
$permissions = [
|
||||
'access administration pages',
|
||||
'view any unpublished content',
|
||||
'use editorial transition create_new_draft',
|
||||
'use editorial transition publish',
|
||||
'create content translations',
|
||||
'create page content',
|
||||
'create url aliases',
|
||||
'edit any page content',
|
||||
'translate any entity',
|
||||
];
|
||||
$this->drupalLogin($this->drupalCreateUser($permissions));
|
||||
// Create a node, add URL alias and publish it.
|
||||
$this->drupalGet('node/add/page');
|
||||
$edit['title[0][value]'] = 'test';
|
||||
$edit['path[0][alias]'] = '/' . $alias;
|
||||
$edit['moderation_state[0][state]'] = 'published';
|
||||
$this->submitForm($edit, 'Save');
|
||||
// Add french translation.
|
||||
$this->drupalGet('node/1/translations');
|
||||
$this->clickLink('Add');
|
||||
$this->submitForm(['moderation_state[0][state]' => 'published'], 'Save (this translation)');
|
||||
// Translation should be saved.
|
||||
$this->assertSession()->pageTextContains('Basic page test has been updated.');
|
||||
// There shouldn't be any validation errors.
|
||||
$this->assertSession()->pageTextNotContains("Either the path '/node/1' is invalid or you do not have access to it.");
|
||||
// Translation should be saved with the given alias.
|
||||
$this->container->get('path_alias.manager')->cacheClear();
|
||||
$translation_alias = $this->container->get('path_alias.manager')->getAliasByPath('/node/1', 'fr');
|
||||
$this->assertSame('/' . $alias, $translation_alias);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Kernel\Migrate\d6;
|
||||
|
||||
use Drupal\path_alias\PathAliasInterface;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
|
||||
use Drupal\Tests\Traits\Core\PathAliasTestTrait;
|
||||
|
||||
/**
|
||||
* URL alias migration.
|
||||
*
|
||||
* @group #slow
|
||||
* @group migrate_drupal_6
|
||||
*/
|
||||
class MigrateUrlAliasTest extends MigrateDrupal6TestBase {
|
||||
|
||||
use PathAliasTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'language',
|
||||
'content_translation',
|
||||
'path',
|
||||
'path_alias',
|
||||
'menu_ui',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('path_alias');
|
||||
$this->installConfig(['node']);
|
||||
$this->installSchema('node', ['node_access']);
|
||||
$this->migrateUsers(FALSE);
|
||||
$this->migrateFields();
|
||||
|
||||
$this->executeMigrations([
|
||||
'language',
|
||||
'd6_node_settings',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a path alias matches a set of conditions.
|
||||
*
|
||||
* @param int $pid
|
||||
* The path alias ID.
|
||||
* @param array $conditions
|
||||
* The path conditions.
|
||||
* @param \Drupal\path_alias\PathAliasInterface $path_alias
|
||||
* The path alias.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
private function assertPath(int $pid, array $conditions, PathAliasInterface $path_alias): void {
|
||||
$this->assertSame($pid, (int) $path_alias->id());
|
||||
$this->assertSame($conditions['alias'], $path_alias->getAlias());
|
||||
$this->assertSame($conditions['langcode'], $path_alias->get('langcode')->value);
|
||||
$this->assertSame($conditions['path'], $path_alias->getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the URL alias migration.
|
||||
*/
|
||||
public function testUrlAlias(): void {
|
||||
$this->executeMigrations([
|
||||
'd6_node',
|
||||
'd6_node_translation',
|
||||
'd6_url_alias',
|
||||
]);
|
||||
$this->checkUrlMigration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the URL alias migration using the node complete migration.
|
||||
*/
|
||||
public function testNodeCompleteUrlAlias(): void {
|
||||
$this->executeMigrations([
|
||||
'd6_node_complete',
|
||||
'd6_url_alias',
|
||||
]);
|
||||
$this->checkUrlMigration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the migration results.
|
||||
*/
|
||||
protected function checkUrlMigration(): void {
|
||||
$id_map = $this->getMigration('d6_url_alias')->getIdMap();
|
||||
// Test that the field exists.
|
||||
$conditions = [
|
||||
'path' => '/node/1',
|
||||
'alias' => '/alias-one',
|
||||
'langcode' => 'af',
|
||||
];
|
||||
$path_alias = $this->loadPathAliasByConditions($conditions);
|
||||
$this->assertPath(1, $conditions, $path_alias);
|
||||
$this->assertSame([['1']], $id_map->lookupDestinationIds([$path_alias->id()]), "Test IdMap");
|
||||
|
||||
$conditions = [
|
||||
'path' => '/node/2',
|
||||
'alias' => '/alias-two',
|
||||
'langcode' => 'en',
|
||||
];
|
||||
$path_alias = $this->loadPathAliasByConditions($conditions);
|
||||
$this->assertPath(2, $conditions, $path_alias);
|
||||
|
||||
// Test that we can re-import using the UrlAlias destination.
|
||||
Database::getConnection('default', 'migrate')
|
||||
->update('url_alias')
|
||||
->fields(['dst' => 'new-url-alias'])
|
||||
->condition('src', 'node/2')
|
||||
->execute();
|
||||
|
||||
\Drupal::database()
|
||||
->update($id_map->mapTableName())
|
||||
->fields(['source_row_status' => MigrateIdMapInterface::STATUS_NEEDS_UPDATE])
|
||||
->execute();
|
||||
$migration = $this->getMigration('d6_url_alias');
|
||||
$this->executeMigration($migration);
|
||||
|
||||
$path_alias = $this->loadPathAliasByConditions(['id' => $path_alias->id()]);
|
||||
$conditions['alias'] = '/new-url-alias';
|
||||
$this->assertPath(2, $conditions, $path_alias);
|
||||
|
||||
$conditions = [
|
||||
'path' => '/node/3',
|
||||
'alias' => '/alias-three',
|
||||
'langcode' => 'und',
|
||||
];
|
||||
$path_alias = $this->loadPathAliasByConditions($conditions);
|
||||
$this->assertPath(3, $conditions, $path_alias);
|
||||
|
||||
$path_alias = $this->loadPathAliasByConditions(['alias' => '/source-noslash']);
|
||||
$conditions = [
|
||||
'path' => '/admin',
|
||||
'alias' => '/source-noslash',
|
||||
'langcode' => 'und',
|
||||
];
|
||||
$this->assertPath(8, $conditions, $path_alias);
|
||||
|
||||
// Tests the URL alias migration with translated nodes.
|
||||
// Alias for the 'The Real McCoy' node in English.
|
||||
$path_alias = $this->loadPathAliasByConditions(['alias' => '/the-real-mccoy']);
|
||||
$this->assertSame('/node/10', $path_alias->getPath());
|
||||
$this->assertSame('en', $path_alias->get('langcode')->value);
|
||||
|
||||
// Alias for the 'The Real McCoy' French translation,
|
||||
// which should now point to node/10 instead of node/11.
|
||||
$path_alias = $this->loadPathAliasByConditions(['alias' => '/le-vrai-mccoy']);
|
||||
$this->assertSame('/node/10', $path_alias->getPath());
|
||||
$this->assertSame('fr', $path_alias->get('langcode')->value);
|
||||
|
||||
// Alias for the 'Abantu zulu' node in Zulu.
|
||||
$path_alias = $this->loadPathAliasByConditions(['alias' => '/abantu-zulu']);
|
||||
$this->assertSame('/node/12', $path_alias->getPath());
|
||||
$this->assertSame('zu', $path_alias->get('langcode')->value);
|
||||
|
||||
// Alias for the 'Abantu zulu' English translation,
|
||||
// which should now point to node/12 instead of node/13.
|
||||
$path_alias = $this->loadPathAliasByConditions(['alias' => '/the-zulu-people']);
|
||||
$this->assertSame('/node/12', $path_alias->getPath());
|
||||
$this->assertSame('en', $path_alias->get('langcode')->value);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Kernel\Migrate\d7;
|
||||
|
||||
/**
|
||||
* Tests URL alias migration.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
class MigrateUrlAliasNoTranslationTest extends MigrateUrlAliasTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->executeMigration('d7_url_alias');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Kernel\Migrate\d7;
|
||||
|
||||
/**
|
||||
* Tests URL alias migration.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
class MigrateUrlAliasTest extends MigrateUrlAliasTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'path_alias',
|
||||
'content_translation',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->executeMigrations([
|
||||
'd7_node_translation',
|
||||
'd7_url_alias',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the URL alias migration with translated nodes.
|
||||
*/
|
||||
public function testUrlAliasWithTranslatedNodes(): void {
|
||||
// Alias for the 'The thing about Deep Space 9' node in English.
|
||||
$path_alias = $this->loadPathAliasByConditions(['alias' => '/deep-space-9']);
|
||||
$this->assertSame('/node/2', $path_alias->getPath());
|
||||
$this->assertSame('en', $path_alias->get('langcode')->value);
|
||||
|
||||
// Alias for the 'The thing about Deep Space 9' Icelandic translation,
|
||||
// which should now point to node/2 instead of node/3.
|
||||
$path_alias = $this->loadPathAliasByConditions(['alias' => '/deep-space-9-is']);
|
||||
$this->assertSame('/node/2', $path_alias->getPath());
|
||||
$this->assertSame('is', $path_alias->get('langcode')->value);
|
||||
|
||||
// Alias for the 'The thing about Firefly' node in Icelandic.
|
||||
$path_alias = $this->loadPathAliasByConditions(['alias' => '/firefly-is']);
|
||||
$this->assertSame('/node/4', $path_alias->getPath());
|
||||
$this->assertSame('is', $path_alias->get('langcode')->value);
|
||||
|
||||
// Alias for the 'The thing about Firefly' English translation,
|
||||
// which should now point to node/4 instead of node/5.
|
||||
$path_alias = $this->loadPathAliasByConditions(['alias' => '/firefly']);
|
||||
$this->assertSame('/node/4', $path_alias->getPath());
|
||||
$this->assertSame('en', $path_alias->get('langcode')->value);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Kernel\Migrate\d7;
|
||||
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
||||
use Drupal\Tests\Traits\Core\PathAliasTestTrait;
|
||||
|
||||
/**
|
||||
* Tests URL alias migration.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
abstract class MigrateUrlAliasTestBase extends MigrateDrupal7TestBase {
|
||||
|
||||
use PathAliasTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'language',
|
||||
'menu_ui',
|
||||
'node',
|
||||
'path',
|
||||
'path_alias',
|
||||
'text',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('path_alias');
|
||||
$this->installConfig('node');
|
||||
$this->installSchema('node', ['node_access']);
|
||||
|
||||
$this->migrateUsers(FALSE);
|
||||
$this->migrateContentTypes();
|
||||
$this->executeMigrations([
|
||||
'language',
|
||||
'd7_node',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the URL alias migration.
|
||||
*/
|
||||
public function testUrlAlias(): void {
|
||||
$path_alias = $this->loadPathAliasByConditions([
|
||||
'path' => '/taxonomy/term/4',
|
||||
'alias' => '/term33',
|
||||
'langcode' => 'und',
|
||||
]);
|
||||
$this->assertSame('/taxonomy/term/4', $path_alias->getPath());
|
||||
$this->assertSame('/term33', $path_alias->getAlias());
|
||||
$this->assertSame('und', $path_alias->language()->getId());
|
||||
|
||||
// Alias with no slash.
|
||||
$path_alias = $this->loadPathAliasByConditions(['alias' => '/source-noSlash']);
|
||||
$this->assertSame('/admin', $path_alias->getPath());
|
||||
$this->assertSame('und', $path_alias->language()->getId());
|
||||
}
|
||||
|
||||
}
|
||||
218
web/core/modules/path/tests/src/Kernel/PathItemTest.php
Normal file
218
web/core/modules/path/tests/src/Kernel/PathItemTest.php
Normal file
@ -0,0 +1,218 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
// cspell:ignore furchtbar
|
||||
|
||||
/**
|
||||
* Tests loading and storing data using PathItem.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
class PathItemTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'path',
|
||||
'path_alias',
|
||||
'node',
|
||||
'user',
|
||||
'system',
|
||||
'language',
|
||||
'content_translation',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('path_alias');
|
||||
|
||||
$this->installSchema('node', ['node_access']);
|
||||
|
||||
NodeType::create([
|
||||
'type' => 'foo',
|
||||
'name' => 'Foo',
|
||||
])->save();
|
||||
|
||||
$this->installConfig(['language']);
|
||||
ConfigurableLanguage::createFromLangcode('de')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating, loading, updating and deleting aliases through PathItem.
|
||||
*/
|
||||
public function testPathItem(): void {
|
||||
/** @var \Drupal\path_alias\AliasRepositoryInterface $alias_repository */
|
||||
$alias_repository = \Drupal::service('path_alias.repository');
|
||||
|
||||
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
|
||||
$node = Node::create([
|
||||
'title' => 'Testing create()',
|
||||
'type' => 'foo',
|
||||
'path' => ['alias' => '/foo'],
|
||||
]);
|
||||
$this->assertFalse($node->get('path')->isEmpty());
|
||||
$this->assertEquals('/foo', $node->get('path')->alias);
|
||||
|
||||
$node->save();
|
||||
$this->assertFalse($node->get('path')->isEmpty());
|
||||
$this->assertEquals('/foo', $node->get('path')->alias);
|
||||
|
||||
$stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId());
|
||||
$this->assertEquals('/foo', $stored_alias['alias']);
|
||||
|
||||
$node_storage->resetCache();
|
||||
|
||||
/** @var \Drupal\node\NodeInterface $loaded_node */
|
||||
$loaded_node = $node_storage->load($node->id());
|
||||
$this->assertFalse($loaded_node->get('path')->isEmpty());
|
||||
$this->assertEquals('/foo', $loaded_node->get('path')->alias);
|
||||
$node_storage->resetCache();
|
||||
$loaded_node = $node_storage->load($node->id());
|
||||
$this->assertEquals('/foo', $loaded_node->get('path')[0]->get('alias')->getValue());
|
||||
|
||||
$node_storage->resetCache();
|
||||
$loaded_node = $node_storage->load($node->id());
|
||||
$values = $loaded_node->get('path')->getValue();
|
||||
$this->assertEquals('/foo', $values[0]['alias']);
|
||||
|
||||
$node_storage->resetCache();
|
||||
$loaded_node = $node_storage->load($node->id());
|
||||
$this->assertEquals('/foo', $loaded_node->path->alias);
|
||||
|
||||
// Add a translation, verify it is being saved as expected.
|
||||
$translation = $loaded_node->addTranslation('de', $loaded_node->toArray());
|
||||
$translation->get('path')->alias = '/furchtbar';
|
||||
$translation->save();
|
||||
|
||||
// Assert the alias on the English node, the German translation, and the
|
||||
// stored aliases.
|
||||
$node_storage->resetCache();
|
||||
$loaded_node = $node_storage->load($node->id());
|
||||
$this->assertEquals('/foo', $loaded_node->path->alias);
|
||||
$translation = $loaded_node->getTranslation('de');
|
||||
$this->assertEquals('/furchtbar', $translation->path->alias);
|
||||
|
||||
$stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId());
|
||||
$this->assertEquals('/foo', $stored_alias['alias']);
|
||||
$stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $translation->language()->getId());
|
||||
$this->assertEquals('/furchtbar', $stored_alias['alias']);
|
||||
|
||||
$loaded_node->get('path')->alias = '/bar';
|
||||
$this->assertFalse($loaded_node->get('path')->isEmpty());
|
||||
$this->assertEquals('/bar', $loaded_node->get('path')->alias);
|
||||
|
||||
$loaded_node->save();
|
||||
$this->assertFalse($loaded_node->get('path')->isEmpty());
|
||||
$this->assertEquals('/bar', $loaded_node->get('path')->alias);
|
||||
|
||||
$node_storage->resetCache();
|
||||
$loaded_node = $node_storage->load($node->id());
|
||||
$this->assertFalse($loaded_node->get('path')->isEmpty());
|
||||
$this->assertEquals('/bar', $loaded_node->get('path')->alias);
|
||||
|
||||
$loaded_node->get('path')->alias = '/bar';
|
||||
$this->assertFalse($loaded_node->get('path')->isEmpty());
|
||||
$this->assertEquals('/bar', $loaded_node->get('path')->alias);
|
||||
|
||||
$loaded_node->save();
|
||||
$this->assertFalse($loaded_node->get('path')->isEmpty());
|
||||
$this->assertEquals('/bar', $loaded_node->get('path')->alias);
|
||||
|
||||
$stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId());
|
||||
$this->assertEquals('/bar', $stored_alias['alias']);
|
||||
|
||||
$old_alias = $alias_repository->lookupByAlias('/foo', $node->language()->getId());
|
||||
$this->assertNull($old_alias);
|
||||
|
||||
// Reload the node to make sure that it is possible to set a value
|
||||
// immediately after loading.
|
||||
$node_storage->resetCache();
|
||||
$loaded_node = $node_storage->load($node->id());
|
||||
$loaded_node->get('path')->alias = '/foobar';
|
||||
$loaded_node->save();
|
||||
|
||||
$node_storage->resetCache();
|
||||
$loaded_node = $node_storage->load($node->id());
|
||||
$this->assertFalse($loaded_node->get('path')->isEmpty());
|
||||
$this->assertEquals('/foobar', $loaded_node->get('path')->alias);
|
||||
$stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId());
|
||||
$this->assertEquals('/foobar', $stored_alias['alias']);
|
||||
|
||||
$old_alias = $alias_repository->lookupByAlias('/bar', $node->language()->getId());
|
||||
$this->assertNull($old_alias);
|
||||
|
||||
$loaded_node->get('path')->alias = '';
|
||||
$this->assertEquals('', $loaded_node->get('path')->alias);
|
||||
|
||||
$loaded_node->save();
|
||||
|
||||
$stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId());
|
||||
$this->assertNull($stored_alias);
|
||||
|
||||
// Check that reading, updating and reading the computed alias again in the
|
||||
// same request works without clearing any caches in between.
|
||||
$loaded_node = $node_storage->load($node->id());
|
||||
$loaded_node->get('path')->alias = '/foo';
|
||||
$loaded_node->save();
|
||||
|
||||
$this->assertFalse($loaded_node->get('path')->isEmpty());
|
||||
$this->assertEquals('/foo', $loaded_node->get('path')->alias);
|
||||
$stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId());
|
||||
$this->assertEquals('/foo', $stored_alias['alias']);
|
||||
|
||||
$loaded_node->get('path')->alias = '/foobar';
|
||||
$loaded_node->save();
|
||||
|
||||
$this->assertFalse($loaded_node->get('path')->isEmpty());
|
||||
$this->assertEquals('/foobar', $loaded_node->get('path')->alias);
|
||||
$stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId());
|
||||
$this->assertEquals('/foobar', $stored_alias['alias']);
|
||||
|
||||
// Check that \Drupal\Core\Field\FieldItemList::equals() for the path field
|
||||
// type.
|
||||
$node = Node::create([
|
||||
'title' => $this->randomString(),
|
||||
'type' => 'foo',
|
||||
'path' => ['alias' => '/foo'],
|
||||
]);
|
||||
$second_node = Node::create([
|
||||
'title' => $this->randomString(),
|
||||
'type' => 'foo',
|
||||
'path' => ['alias' => '/foo'],
|
||||
]);
|
||||
$this->assertTrue($node->get('path')->equals($second_node->get('path')));
|
||||
|
||||
// Change the alias for the second node to a different one and try again.
|
||||
$second_node->get('path')->alias = '/foobar';
|
||||
$this->assertFalse($node->get('path')->equals($second_node->get('path')));
|
||||
|
||||
// Test the generateSampleValue() method.
|
||||
$node = Node::create([
|
||||
'title' => $this->randomString(),
|
||||
'type' => 'foo',
|
||||
'path' => ['alias' => '/foo'],
|
||||
]);
|
||||
$node->save();
|
||||
$path_field = $node->get('path');
|
||||
$path_field->generateSampleItems();
|
||||
$node->save();
|
||||
$this->assertStringStartsWith('/', $node->get('path')->alias);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Kernel;
|
||||
|
||||
use Drupal\content_translation_test\Entity\EntityTestTranslatableUISkip;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests path alias deletion when there is no canonical link template.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
class PathNoCanonicalLinkTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'path',
|
||||
'content_translation_test',
|
||||
'language',
|
||||
'entity_test',
|
||||
'user',
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test');
|
||||
$this->installEntitySchema('entity_test_mul');
|
||||
|
||||
// Adding german language.
|
||||
ConfigurableLanguage::createFromLangcode('de')->save();
|
||||
|
||||
$this->config('language.types')->setData([
|
||||
'configurable' => ['language_interface'],
|
||||
'negotiation' => ['language_interface' => ['enabled' => ['language-url' => 0]]],
|
||||
])->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for no canonical link templates.
|
||||
*/
|
||||
public function testNoCanonicalLinkTemplate(): void {
|
||||
$entity_type = EntityTestTranslatableUISkip::create([
|
||||
'name' => 'name english',
|
||||
'language' => 'en',
|
||||
]);
|
||||
$entity_type->save();
|
||||
|
||||
$entity_type->addTranslation('de', ['name' => 'name german']);
|
||||
$entity_type->save();
|
||||
$this->assertCount(2, $entity_type->getTranslationLanguages());
|
||||
|
||||
$entity_type->removeTranslation('de');
|
||||
$entity_type->save();
|
||||
$this->assertCount(1, $entity_type->getTranslationLanguages());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Kernel\Plugin\migrate\source\d6;
|
||||
|
||||
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
|
||||
|
||||
/**
|
||||
* Tests the d6_url_alias source plugin.
|
||||
*
|
||||
* @covers \Drupal\path\Plugin\migrate\source\d6\UrlAlias
|
||||
* @group path
|
||||
*/
|
||||
class UrlAliasTest extends MigrateSqlSourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['migrate_drupal', 'path'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function providerSource() {
|
||||
$tests = [];
|
||||
|
||||
// The source data.
|
||||
$tests[0]['source_data']['url_alias'] = [
|
||||
[
|
||||
'pid' => 1,
|
||||
'src' => 'node/1',
|
||||
'dst' => 'test-article',
|
||||
'language' => 'en',
|
||||
],
|
||||
[
|
||||
'pid' => 2,
|
||||
'src' => 'node/2',
|
||||
'dst' => 'another-alias',
|
||||
'language' => 'en',
|
||||
],
|
||||
];
|
||||
|
||||
// The expected results.
|
||||
$tests[0]['expected_data'] = $tests[0]['source_data']['url_alias'];
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Kernel\Plugin\migrate\source\d7;
|
||||
|
||||
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
|
||||
|
||||
/**
|
||||
* Tests the d7_url_alias source plugin.
|
||||
*
|
||||
* @covers \Drupal\path\Plugin\migrate\source\d7\UrlAlias
|
||||
* @group path
|
||||
*/
|
||||
class UrlAliasTest extends MigrateSqlSourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['migrate_drupal', 'path'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function providerSource() {
|
||||
$tests = [];
|
||||
|
||||
// The source data.
|
||||
$tests[0]['source_data']['url_alias'] = [
|
||||
[
|
||||
'pid' => 1,
|
||||
'source' => 'node/1',
|
||||
'alias' => 'test-article',
|
||||
'language' => 'en',
|
||||
],
|
||||
[
|
||||
'pid' => 2,
|
||||
'source' => 'node/2',
|
||||
'alias' => 'another-alias',
|
||||
'language' => 'en',
|
||||
],
|
||||
];
|
||||
|
||||
// The expected results.
|
||||
$tests[0]['expected_data'] = $tests[0]['source_data']['url_alias'];
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Unit\Field;
|
||||
|
||||
use Drupal\Tests\Core\Field\BaseFieldDefinitionTestBase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Field\BaseFieldDefinition
|
||||
* @group path
|
||||
*/
|
||||
class PathFieldDefinitionTest extends BaseFieldDefinitionTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getPluginId(): string {
|
||||
return 'path';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getModuleAndPath(): array {
|
||||
return ['path', dirname(__DIR__, 4)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getColumns
|
||||
* @covers ::getSchema
|
||||
*/
|
||||
public function testGetColumns(): void {
|
||||
$this->assertSame([], $this->definition->getColumns());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\path\Unit\migrate\process;
|
||||
|
||||
use Drupal\path\Plugin\migrate\process\PathSetTranslated;
|
||||
use Drupal\Tests\migrate\Unit\process\MigrateProcessTestCase;
|
||||
|
||||
/**
|
||||
* Tests the path_set_translated process plugin.
|
||||
*
|
||||
* @group path
|
||||
* @coversDefaultClass \Drupal\path\Plugin\migrate\process\PathSetTranslated
|
||||
*/
|
||||
class PathSetTranslatedTest extends MigrateProcessTestCase {
|
||||
|
||||
/**
|
||||
* Tests the transform method.
|
||||
*
|
||||
* @param string $path
|
||||
* The path to test.
|
||||
* @param mixed $node_translation
|
||||
* The translated node value to test.
|
||||
* @param string $expected_result
|
||||
* The expected result.
|
||||
*
|
||||
* @covers ::transform
|
||||
*
|
||||
* @dataProvider transformDataProvider
|
||||
*/
|
||||
public function testTransform($path, $node_translation, $expected_result): void {
|
||||
$plugin = new PathSetTranslated([], 'path_set_translated', []);
|
||||
$this->assertSame($expected_result, $plugin->transform([$path, $node_translation], $this->migrateExecutable, $this->row, 'destination_property'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for the testTransform method.
|
||||
*
|
||||
* @return array
|
||||
* The data.
|
||||
*/
|
||||
public static function transformDataProvider() {
|
||||
return [
|
||||
'non-node-path' => [
|
||||
'path' => '/non-node-path',
|
||||
'node_translation' => [1, 'en'],
|
||||
'expected_result' => '/non-node-path',
|
||||
],
|
||||
'no_translated_node_1' => [
|
||||
'path' => '/node/1',
|
||||
'node_translation' => 'INVALID_NID',
|
||||
'expected_result' => '/node/1',
|
||||
],
|
||||
'no_translated_node_2' => [
|
||||
'path' => '/node/1',
|
||||
'node_translation' => NULL,
|
||||
'expected_result' => '/node/1',
|
||||
],
|
||||
'no_translated_node_3' => [
|
||||
'path' => '/node/1',
|
||||
'node_translation' => FALSE,
|
||||
'expected_result' => '/node/1',
|
||||
],
|
||||
'valid_transform' => [
|
||||
'path' => '/node/1',
|
||||
'node_translation' => [3, 'en'],
|
||||
'expected_result' => '/node/3',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user