class PageContextTest

Tests the PageContext Top Bar item build output.

Attributes

#[CoversClass(PageContext::class)] #[Group('navigation')]

Hierarchy

Expanded class hierarchy of PageContextTest

File

core/modules/navigation/tests/src/Unit/PageContextTest.php, line 26

Namespace

Drupal\Tests\navigation\Unit
View source
class PageContextTest extends UnitTestCase {
  use StringTranslationTrait;
  
  /**
   * {@inheritdoc}
   */
  protected function setUp() : void {
    parent::setUp();
    $container = new ContainerBuilder();
    $container->set('string_translation', $this->getStringTranslationStub());
    \Drupal::setContainer($container);
  }
  
  /**
   * Tests the build method when no entity is present on the route.
   */
  public function testBuildWhenNoEntityOnRoute() : void {
    $route_helper = $this->prophesize(EntityRouteHelper::class);
    $route_helper->getContentEntityFromRoute()
      ->willReturn(NULL);
    $entity_repository = $this->prophesize(EntityRepositoryInterface::class)
      ->reveal();
    $moderation_information = $this->prophesize(ModerationInformationInterface::class)
      ->reveal();
    $plugin = new PageContext([], 'page_context', [], $route_helper->reveal(), $entity_repository, $moderation_information);
    $build = $plugin->build();
    $this->assertSame([
      '#cache' => [
        'contexts' => [
          'route',
        ],
      ],
    ], $build);
  }
  
  /**
   * Tests the build of an entity label within the page context plugin.
   *
   * @param mixed $label_value
   *   The return value of the entity label method. Can be a string,
   *   render array, stringable object, or an invalid value.
   * @param string|array|null $expected
   *   The expected parsed label value for the page context. If the label is
   *   invalid, the value should be NULL.
   */
  public function testBuildEntityLabel(mixed $label_value, string|array|null $expected) : void {
    // Route returns an entity with different label return types.
    $entity = $this->prophesize(ContentEntityInterface::class);
    $entity->label()
      ->willReturn($label_value);
    $route_helper = $this->prophesize(EntityRouteHelper::class);
    $route_helper->getContentEntityFromRoute()
      ->willReturn($entity->reveal());
    $entity_repository = $this->prophesize(EntityRepositoryInterface::class)
      ->reveal();
    $moderation_information = $this->prophesize(ModerationInformationInterface::class)
      ->reveal();
    $plugin = new PageContext([], 'page_context', [], $route_helper->reveal(), $entity_repository, $moderation_information);
    $build = $plugin->build();
    $expected_base = [
      '#cache' => [
        'contexts' => [
          'route',
        ],
      ],
    ];
    if ($expected === NULL) {
      // Invalid label → no components added.
      $this->assertSame($expected_base, $build);
      return;
    }
    // Title component should be present as the first item.
    $this->assertArrayHasKey(0, $build);
    $this->assertSame('component', $build[0]['#type']);
    $this->assertSame('navigation:title', $build[0]['#component']);
    $this->assertSame('database', $build[0]['#props']['icon']);
    $this->assertSame($expected, $build[0]['#slots']['content']);
  }
  
  /**
   * Data provider for entity label scenarios.
   *
   * @return array
   *   [label_return_value, expected]
   */
  public static function entityLabelProvider() : array {
    $stringable = new class  implements \Stringable {
      
      /**
       * Dummy string method.
       *
       * @return string
       *   The dummy entity label.
       */
      public function __toString() : string {
        return 'Stringable Label';
      }

};
    return [
      'string' => [
        'My Label',
        'My Label',
      ],
      'stringable' => [
        $stringable,
        'Stringable Label',
      ],
      'render_array' => [
        [
          '#markup' => 'Rendered Label',
        ],
        NULL,
      ],
      'invalid' => [
        new \stdClass(),
        NULL,
      ],
    ];
  }
  
  /**
   * Tests the status badge for published and unpublished entities.
   */
  public function testBuildStatusBadge() : void {
    $entity = $this->prophesize(ContentEntityInterface::class);
    $entity->willImplement(EntityPublishedInterface::class);
    $entity->isPublished()
      ->willReturn(TRUE);
    $entity->label()
      ->willReturn('Published Title');
    $route_helper = $this->prophesize(EntityRouteHelper::class);
    $route_helper->getContentEntityFromRoute()
      ->willReturn($entity->reveal());
    $entity_repository = $this->prophesize(EntityRepositoryInterface::class)
      ->reveal();
    $published_plugin = new PageContext([], 'page_context', [], $route_helper->reveal(), $entity_repository, NULL);
    $build = $published_plugin->build();
    $this->assertSame('Published Title', $build[0]['#slots']['content']);
    $this->assertSame('navigation:badge', $build[1]['#component']);
    $this->assertSame('Published', $build[1]['#slots']['label']);
    $this->assertSame('success', $build[1]['#props']['status']);
    // Now assert the Unpublished path.
    $entity->isPublished()
      ->willReturn(FALSE);
    $entity->label()
      ->willReturn('Unpublished Title');
    $route_helper->getContentEntityFromRoute()
      ->willReturn($entity->reveal());
    $plugin = new PageContext([], 'page_context', [], $route_helper->reveal(), $entity_repository, NULL);
    $build = $plugin->build();
    $this->assertSame('Unpublished Title', $build[0]['#slots']['content']);
    $this->assertSame('Unpublished', $build[1]['#slots']['label']);
    $this->assertSame('info', $build[1]['#props']['status']);
  }
  
  /**
   * Tests content moderation build output with no pending revisions.
   */
  public function testBuildContentModerationNoPending() : void {
    // Mock moderated content entity with state 'draft'.
    $entity = $this->prophesize(ContentEntityInterface::class);
    $entity->get('moderation_state')
      ->willReturn((object) [
      'value' => 'draft',
    ]);
    $entity->isDefaultRevision()
      ->willReturn(TRUE);
    $entity->getEntityTypeId()
      ->willReturn('node');
    $entity->id()
      ->willReturn('1');
    $entity->label()
      ->willReturn('Example Title');
    // Workflow chain: workflow -> type plugin -> state('draft')->label() => 'Draft'.
    $state = $this->prophesize(StateInterface::class);
    $state->label()
      ->willReturn('Draft');
    $type = $this->prophesize(WorkflowTypeInterface::class);
    $type->getState('draft')
      ->willReturn($state->reveal());
    $workflow = $this->prophesize(WorkflowInterface::class);
    $workflow->getTypePlugin()
      ->willReturn($type->reveal());
    $moderation = $this->prophesize(ModerationInformationInterface::class);
    $moderation->isModeratedEntity($entity->reveal())
      ->willReturn(TRUE);
    $moderation->getWorkflowForEntity($entity->reveal())
      ->willReturn($workflow->reveal());
    $moderation->hasPendingRevision($entity->reveal())
      ->willReturn(FALSE);
    $route_helper = $this->prophesize(EntityRouteHelper::class);
    $route_helper->getContentEntityFromRoute()
      ->willReturn($entity->reveal());
    $entity_repository = $this->prophesize(EntityRepositoryInterface::class)
      ->reveal();
    $plugin = new PageContext([], 'page_context', [], $route_helper->reveal(), $entity_repository, $moderation->reveal());
    $build = $plugin->build();
    // Title component.
    $this->assertSame('Example Title', $build[0]['#slots']['content']);
    // Badge component with label Draft, default status info (not published interface).
    $this->assertSame('navigation:badge', $build[1]['#component']);
    $this->assertSame('Draft', $build[1]['#slots']['label']);
    $this->assertSame('info', $build[1]['#props']['status']);
  }
  
  /**
   * Tests the content moderation build when is active with a pending entity.
   */
  public function testBuildContentModerationWithPendingActive() : void {
    // Entity current state is 'draft', active is 'published'.
    $entity = $this->prophesize(ContentEntityInterface::class);
    $entity->get('moderation_state')
      ->willReturn((object) [
      'value' => 'draft',
    ]);
    $entity->isDefaultRevision()
      ->willReturn(TRUE);
    $entity->getEntityTypeId()
      ->willReturn('node');
    $entity->id()
      ->willReturn('1');
    $entity->label()
      ->willReturn('Example Title');
    $active = $this->prophesize(ContentEntityInterface::class);
    $active->get('moderation_state')
      ->willReturn((object) [
      'value' => 'published',
    ]);
    // State objects and labels.
    $draft_state = $this->prophesize(StateInterface::class);
    $draft_state->label()
      ->willReturn('Draft');
    $published_state = $this->prophesize(StateInterface::class);
    $published_state->label()
      ->willReturn('Published');
    $type = $this->prophesize(WorkflowTypeInterface::class);
    $type->getState('draft')
      ->willReturn($draft_state->reveal());
    $type->getState('published')
      ->willReturn($published_state->reveal());
    $workflow = $this->prophesize(WorkflowInterface::class);
    $workflow->getTypePlugin()
      ->willReturn($type->reveal());
    $moderation = $this->prophesize(ModerationInformationInterface::class);
    $moderation->isModeratedEntity($entity->reveal())
      ->willReturn(TRUE);
    $moderation->getWorkflowForEntity($entity->reveal())
      ->willReturn($workflow->reveal());
    $moderation->getWorkflowForEntity($active->reveal())
      ->willReturn($workflow->reveal());
    $moderation->hasPendingRevision($entity->reveal())
      ->willReturn(TRUE);
    $entity_repository = $this->prophesize(EntityRepositoryInterface::class);
    $entity_repository->getActive('node', '1')
      ->willReturn($active->reveal());
    $route_helper = $this->prophesize(EntityRouteHelper::class);
    $route_helper->getContentEntityFromRoute()
      ->willReturn($entity->reveal());
    $plugin = new PageContext([], 'page_context', [], $route_helper->reveal(), $entity_repository->reveal(), $moderation->reveal());
    $build = $plugin->build();
    $this->assertSame('Example Title', $build[0]['#slots']['content']);
    $this->assertSame('navigation:badge', $build[1]['#component']);
    $this->assertSame('Draft (Published available)', $build[1]['#slots']['label']);
    // Status still defaults to info as entity might not be EntityPublishedInterface here.
    $this->assertSame('info', $build[1]['#props']['status']);
  }
  
  /**
   * Tests the behavior of a plugin with no valid badge present.
   */
  public function testNoValidBadge() : void {
    $entity = $this->prophesize(ContentEntityInterface::class);
    $entity->label()
      ->willReturn('Simple Title');
    $route_helper = $this->prophesize(EntityRouteHelper::class);
    $route_helper->getContentEntityFromRoute()
      ->willReturn($entity->reveal());
    $entity_repository = $this->prophesize(EntityRepositoryInterface::class)
      ->reveal();
    $plugin = new PageContext([], 'page_context', [], $route_helper->reveal(), $entity_repository, NULL);
    $build = $plugin->build();
    $this->assertSame('Simple Title', $build[0]['#slots']['content']);
    // Only one component (title) should be present.
    $this->assertArrayNotHasKey(1, $build);
  }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
ExpectDeprecationTrait::expectDeprecation public function Adds an expected deprecation.
ExpectDeprecationTrait::setUpErrorHandler public function Sets up the test error handler.
ExpectDeprecationTrait::tearDownErrorHandler public function Tears down the test error handler.
PageContextTest::entityLabelProvider public static function Data provider for entity label scenarios.
PageContextTest::setUp protected function Overrides UnitTestCase::setUp
PageContextTest::testBuildContentModerationNoPending public function Tests content moderation build output with no pending revisions.
PageContextTest::testBuildContentModerationWithPendingActive public function Tests the content moderation build when is active with a pending entity.
PageContextTest::testBuildEntityLabel public function Tests the build of an entity label within the page context plugin.
PageContextTest::testBuildStatusBadge public function Tests the status badge for published and unpublished entities.
PageContextTest::testBuildWhenNoEntityOnRoute public function Tests the build method when no entity is present on the route.
PageContextTest::testNoValidBadge public function Tests the behavior of a plugin with no valid badge present.
RandomGeneratorTrait::getRandomGenerator protected function Gets the random generator for the utility methods.
RandomGeneratorTrait::randomMachineName protected function Generates a unique random string containing letters and numbers.
RandomGeneratorTrait::randomObject public function Generates a random PHP object.
RandomGeneratorTrait::randomString public function Generates a pseudo-random string of ASCII characters of codes 32 to 126.
StringTranslationTrait::$stringTranslation protected property The string translation service. 3
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language. 1
UnitTestCase::$root protected property The app root.
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::setDebugDumpHandler public static function Registers the dumper CLI handler when the DebugDump extension is enabled.
UnitTestCase::setupMockIterator protected function Set up a traversable class mock to return specific items when iterated.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.