Initial Drupal 11 with DDEV setup

This commit is contained in:
gluebox
2025-10-08 11:39:17 -04:00
commit 89ef74b305
25344 changed files with 2599172 additions and 0 deletions

View File

@ -0,0 +1,5 @@
name: 'Path test miscellaneous utilities'
type: module
description: 'Utilities for path testing'
package: Testing
version: VERSION

View File

@ -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();
}
}

View File

@ -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

View File

@ -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;
}
}

View 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 {}

View 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);
}
}

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

View File

@ -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);
}
}
}

View 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);
}
}

View File

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

View File

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

View File

@ -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());
}
}

View File

@ -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);
}
}

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

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

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

View File

@ -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);
}
}

View File

@ -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());
}
}

View 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);
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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',
],
];
}
}