From d2eb0235ccea45707c168feab2dba3d9ec7f941f Mon Sep 17 00:00:00 2001 From: catch <catch@35733.no-reply.drupal.org> Date: Wed, 10 May 2023 15:23:22 +0100 Subject: [PATCH] Issue #3130107 by stefanos.petrakis, rpayanm, smustgrave, jungle: Extend unit test coverage for LanguageNegotiationContentEntity --- .../LanguageNegotiationContentEntityTest.php | 204 ++++++++++++++---- .../LanguageNegotiationTestBase.php | 34 +++ 2 files changed, 199 insertions(+), 39 deletions(-) create mode 100644 core/modules/language/tests/src/Unit/Plugin/LanguageNegotiation/LanguageNegotiationTestBase.php diff --git a/core/modules/language/tests/src/Unit/Plugin/LanguageNegotiation/LanguageNegotiationContentEntityTest.php b/core/modules/language/tests/src/Unit/Plugin/LanguageNegotiation/LanguageNegotiationContentEntityTest.php index 907a92e683a5..0f26e1da12af 100644 --- a/core/modules/language/tests/src/Unit/Plugin/LanguageNegotiation/LanguageNegotiationContentEntityTest.php +++ b/core/modules/language/tests/src/Unit/Plugin/LanguageNegotiation/LanguageNegotiationContentEntityTest.php @@ -2,15 +2,18 @@ namespace Drupal\Tests\language\Unit\Plugin\LanguageNegotiation; -use Symfony\Component\HttpFoundation\Request; use Drupal\Core\Cache\Context\CacheContextsManager; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Entity\EntityTypeManager; use Drupal\Core\Language\LanguageInterface; -use Drupal\Core\Session\AccountInterface; +use Drupal\Core\Render\BubbleableMetadata; +use Drupal\Core\Url; use Drupal\language\ConfigurableLanguageManagerInterface; -use Drupal\Tests\UnitTestCase; use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\ServerBag; +use Symfony\Component\Routing\Route; /** * Tests the LanguageNegotiationContentEntity plugin class. @@ -19,7 +22,7 @@ * @coversDefaultClass \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity * @see \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity */ -class LanguageNegotiationContentEntityTest extends UnitTestCase { +class LanguageNegotiationContentEntityTest extends LanguageNegotiationTestBase { /** * An array of mock LanguageInterface objects. @@ -36,11 +39,11 @@ class LanguageNegotiationContentEntityTest extends UnitTestCase { protected $languageManager; /** - * A mock object implementing the AccountInterface. - * - * @var \Drupal\Core\Session\AccountInterface + * {@inheritdoc} */ - protected $user; + protected function getPluginClass(): string { + return LanguageNegotiationContentEntity::class; + } /** * {@inheritdoc} @@ -53,32 +56,39 @@ protected function setUp(): void { $language_de->expects($this->any()) ->method('getId') ->will($this->returnValue('de')); + $language_de->expects($this->any()) + ->method('getName') + ->will($this->returnValue('German')); $language_en = $this->createMock(LanguageInterface::class); $language_en->expects($this->any()) ->method('getId') ->will($this->returnValue('en')); - $languages = [ + $language_en->expects($this->any()) + ->method('getName') + ->will($this->returnValue('English')); + $this->languages = [ 'de' => $language_de, 'en' => $language_en, ]; - $this->languages = $languages; - $language_manager = $this->getMockBuilder(ConfigurableLanguageManagerInterface::class) - ->getMock(); + $language_manager = $this->createMock(ConfigurableLanguageManagerInterface::class); $language_manager->expects($this->any()) ->method('getLanguages') - ->will($this->returnValue($languages)); + ->will($this->returnValue($this->languages)); + $language_manager->expects($this->any()) + ->method('getNativeLanguages') + ->will($this->returnValue($this->languages)); $this->languageManager = $language_manager; - $this->user = $this->getMockBuilder(AccountInterface::class) - ->getMock(); + $container = new ContainerBuilder(); - $cache_contexts_manager = $this->getMockBuilder(CacheContextsManager::class) - ->disableOriginalConstructor() - ->getMock(); + $cache_contexts_manager = $this->createMock(CacheContextsManager::class); $cache_contexts_manager->method('assertValidTokens')->willReturn(TRUE); - $container = new ContainerBuilder(); $container->set('cache_contexts_manager', $cache_contexts_manager); + + $entityTypeManager = $this->createMock(EntityTypeManager::class); + $container->set('entity_type.manager', $entityTypeManager); + \Drupal::setContainer($container); } @@ -86,32 +96,148 @@ protected function setUp(): void { * @covers ::getLangcode */ public function testGetLangcode() { - $entityTypeManagerMock = $this->getMockBuilder(EntityTypeManager::class) - ->disableOriginalConstructor() - ->getMock(); - $languageNegotiationContentEntity = new LanguageNegotiationContentEntity($entityTypeManagerMock); - $languageNegotiationContentEntity->setLanguageManager($this->languageManager); + $languageNegotiationContentEntity = $this->createLanguageNegotiationPlugin(); - // Case 1: NULL request object argument. - $this->assertNull($languageNegotiationContentEntity->getLangcode()); + // Case 1: Empty request. + $this->assertEquals(NULL, $languageNegotiationContentEntity->getLangcode()); - // Case 2: A request object is available, but the languageManager is not - // set. - $request = Request::create('/foo', 'GET'); - $this->assertNull($languageNegotiationContentEntity->getLangcode($request)); + // Case 2: A request is available, but the languageManager is not set and + // the static::QUERY_PARAMETER is not provided as a named parameter. + $request = Request::create('/de/foo', 'GET'); + $request->query = new ParameterBag(); + $this->assertEquals(NULL, $languageNegotiationContentEntity->getLangcode($request)); - // Case 3: A request object is available, but static::QUERY_PARAMETER is - // set to a non-enabled language. - $request = Request::create('/foo', 'GET', - [LanguageNegotiationContentEntity::QUERY_PARAMETER => 'it']); + // Case 3: A request is available, the languageManager is set, but the + // static::QUERY_PARAMETER is not provided as a named parameter. + $languageNegotiationContentEntity->setLanguageManager($this->languageManager); + $this->assertEquals(NULL, $languageNegotiationContentEntity->getLangcode($request)); + + // Case 4: A request is available, the languageManager is set and the + // static::QUERY_PARAMETER is provided as a named parameter. + $expectedLangcode = 'de'; + $request->query->set(LanguageNegotiationContentEntity::QUERY_PARAMETER, $expectedLangcode); + $this->assertEquals($expectedLangcode, $languageNegotiationContentEntity->getLangcode($request)); + + // Case 5: A request is available, the languageManager is set and the + // static::QUERY_PARAMETER is provided as a named parameter with a given + // langcode that is not one of the system supported ones. + $unknownLangcode = 'xx'; + $request->query->set(LanguageNegotiationContentEntity::QUERY_PARAMETER, $unknownLangcode); $this->assertNull($languageNegotiationContentEntity->getLangcode($request)); + } - // Case 4: A request object is available and static::QUERY_PARAMETER is - // set to an enabled language. - $request = Request::create('/foo', 'GET', - [LanguageNegotiationContentEntity::QUERY_PARAMETER => 'de']); - $this->assertSame('de', $languageNegotiationContentEntity->getLangcode($request)); + /** + * @covers ::processOutbound + */ + public function testProcessOutbound() { + + // Case 1: Not all processing conditions are met. + $languageNegotiationContentEntityMock = $this->createPartialMock($this->getPluginClass(), + ['hasLowerLanguageNegotiationWeight', 'meetsContentEntityRoutesCondition']); + $languageNegotiationContentEntityMock->expects($this->exactly(2)) + ->method('hasLowerLanguageNegotiationWeight') + ->willReturnOnConsecutiveCalls( + FALSE, + TRUE + ); + $languageNegotiationContentEntityMock->expects($this->once()) + ->method('meetsContentEntityRoutesCondition') + ->willReturnOnConsecutiveCalls( + FALSE + ); + $options = []; + $path = $this->randomMachineName(); + + // Case 1a: Empty request. + $this->assertEquals($path, $languageNegotiationContentEntityMock->processOutbound($path)); + $request = Request::create('/foo', 'GET'); + $request->server = new ServerBag(); + // Case 1b: Missing the route key in $options. + $this->assertEquals($path, $languageNegotiationContentEntityMock->processOutbound($path, $options, $request)); + $options = ['route' => $this->createMock(Route::class)]; + // Case 1c: hasLowerLanguageNegotiationWeight() returns FALSE. + $this->assertEquals($path, $languageNegotiationContentEntityMock->processOutbound($path, $options, $request)); + // Case 1d: meetsContentEntityRoutesCondition() returns FALSE. + $this->assertEquals($path, $languageNegotiationContentEntityMock->processOutbound($path, $options, $request)); + + // Case 2: Cannot figure out the langcode. + $languageNegotiationContentEntityMock = $this->createPartialMock($this->getPluginClass(), + ['hasLowerLanguageNegotiationWeight', 'meetsContentEntityRoutesCondition', 'getLangcode']); + $languageNegotiationContentEntityMock->expects($this->any()) + ->method('hasLowerLanguageNegotiationWeight') + ->will($this->returnValue(TRUE)); + $languageNegotiationContentEntityMock->expects($this->any()) + ->method('meetsContentEntityRoutesCondition') + ->will($this->returnValue(TRUE)); + $languageNegotiationContentEntityMock->expects($this->exactly(2)) + ->method('getLangcode') + ->willReturnOnConsecutiveCalls( + NULL, + 'de' + ); + $this->assertEquals($path, $languageNegotiationContentEntityMock->processOutbound($path, $options, $request)); + + // Case 3: Can figure out the langcode. + // Case 3a: via $options['language']. + $options['language'] = $this->languages['en']; + $options['query'] = NULL; + $bubbleableMetadataMock = $this->createMock(BubbleableMetadata::class); + $bubbleableMetadataMock->expects($this->exactly(3)) + ->method('addCacheContexts') + ->with(['url.query_args:' . LanguageNegotiationContentEntity::QUERY_PARAMETER]); + $this->assertEquals($path, $languageNegotiationContentEntityMock->processOutbound($path, $options, $request, $bubbleableMetadataMock)); + $this->assertFalse(isset($options['language'])); + $this->assertTrue(isset($options['query'][LanguageNegotiationContentEntity::QUERY_PARAMETER])); + $this->assertEquals('en', $options['query'][LanguageNegotiationContentEntity::QUERY_PARAMETER]); + + // Case 3a1: via $options['language'] with an additional $options['query'][static::QUERY_PARAMETER]. + $options['language'] = $this->languages['en']; + $options['query'][LanguageNegotiationContentEntity::QUERY_PARAMETER] = 'xx'; + $this->assertEquals($path, $languageNegotiationContentEntityMock->processOutbound($path, $options, $request, $bubbleableMetadataMock)); + $this->assertFalse(isset($options['language'])); + $this->assertEquals('xx', $options['query'][LanguageNegotiationContentEntity::QUERY_PARAMETER]); + + // Case 3b: via getLangcode(). + unset($options['query'][LanguageNegotiationContentEntity::QUERY_PARAMETER]); + $this->assertEquals($path, $languageNegotiationContentEntityMock->processOutbound($path, $options, $request, $bubbleableMetadataMock)); + $this->assertEquals('de', $options['query'][LanguageNegotiationContentEntity::QUERY_PARAMETER]); + } + + /** + * @covers ::getLanguageSwitchLinks + */ + public function testGetLanguageSwitchLinks() { + $languageNegotiationContentEntity = $this->createLanguageNegotiationPlugin(); + $languageNegotiationContentEntity->setLanguageManager($this->languageManager); + $request = Request::create('/foo', 'GET', ['param1' => 'xyz']); + $url = Url::fromUri('base:' . $this->randomMachineName()); + + $expectedLanguageSwitchLinksArray = [ + 'de' => [ + 'url' => $url, + 'title' => $this->languages['de']->getName(), + 'attributes' => ['class' => ['language-link']], + 'query' => [ + LanguageNegotiationContentEntity::QUERY_PARAMETER => 'de', + 'param1' => 'xyz', + ], + ], + 'en' => [ + 'url' => $url, + 'title' => $this->languages['en']->getName(), + 'attributes' => ['class' => ['language-link']], + 'query' => [ + LanguageNegotiationContentEntity::QUERY_PARAMETER => 'en', + 'param1' => 'xyz', + ], + ], + ]; + $providedLanguageSwitchLinksArray = $languageNegotiationContentEntity->getLanguageSwitchLinks($request, $this->randomMachineName(), $url); + $this->assertEquals( + $expectedLanguageSwitchLinksArray, + $providedLanguageSwitchLinksArray + ); } } diff --git a/core/modules/language/tests/src/Unit/Plugin/LanguageNegotiation/LanguageNegotiationTestBase.php b/core/modules/language/tests/src/Unit/Plugin/LanguageNegotiation/LanguageNegotiationTestBase.php new file mode 100644 index 000000000000..ce2ab9b09a0b --- /dev/null +++ b/core/modules/language/tests/src/Unit/Plugin/LanguageNegotiation/LanguageNegotiationTestBase.php @@ -0,0 +1,34 @@ +<?php + +namespace Drupal\Tests\language\Unit\Plugin\LanguageNegotiation; + +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Tests\UnitTestCase; + +/** + * Base class used for testing the various LanguageNegotiation plugins. + * + * @group language + */ +abstract class LanguageNegotiationTestBase extends UnitTestCase { + + /** + * Returns the plugin class to use for creating the language negotiation plugin. + * + * @return string + * The plugin class name. + */ + abstract protected function getPluginClass(): string; + + /** + * Creates a @LanguageNegotiation plugin using the factory ::create method. + * + * @return \Drupal\language\LanguageNegotiationMethodInterface + */ + protected function createLanguageNegotiationPlugin(array $configuration = [], $plugin_definition = NULL) { + $class = $this->getPluginClass(); + $this->assertTrue(in_array(ContainerFactoryPluginInterface::class, class_implements($class))); + return $class::create(\Drupal::getContainer(), $configuration, $class::METHOD_ID, $plugin_definition); + } + +} -- GitLab