class LayoutPluginManagerTest

Same name in this branch
  1. 10 core/tests/Drupal/KernelTests/Core/Layout/LayoutPluginManagerTest.php \Drupal\KernelTests\Core\Layout\LayoutPluginManagerTest
Same name and namespace in other branches
  1. 9 core/tests/Drupal/Tests/Core/Layout/LayoutPluginManagerTest.php \Drupal\Tests\Core\Layout\LayoutPluginManagerTest
  2. 8.9.x core/tests/Drupal/Tests/Core/Layout/LayoutPluginManagerTest.php \Drupal\Tests\Core\Layout\LayoutPluginManagerTest
  3. 11.x core/tests/Drupal/Tests/Core/Layout/LayoutPluginManagerTest.php \Drupal\Tests\Core\Layout\LayoutPluginManagerTest
  4. 11.x core/tests/Drupal/KernelTests/Core/Layout/LayoutPluginManagerTest.php \Drupal\KernelTests\Core\Layout\LayoutPluginManagerTest

@coversDefaultClass \Drupal\Core\Layout\LayoutPluginManager
@group Layout

Hierarchy

Expanded class hierarchy of LayoutPluginManagerTest

File

core/tests/Drupal/Tests/Core/Layout/LayoutPluginManagerTest.php, line 31

Namespace

Drupal\Tests\Core\Layout
View source
class LayoutPluginManagerTest extends UnitTestCase {
  
  /**
   * The module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;
  
  /**
   * The theme handler.
   *
   * @var \Drupal\Core\Extension\ThemeHandlerInterface
   */
  protected $themeHandler;
  
  /**
   * The theme manager.
   *
   * @var \Drupal\Core\Theme\ThemeManagerInterface
   */
  protected $themeManager;
  
  /**
   * Cache backend instance.
   *
   * @var \Drupal\Core\Cache\CacheBackendInterface
   */
  protected $cacheBackend;
  
  /**
   * The layout plugin manager.
   *
   * @var \Drupal\Core\Layout\LayoutPluginManagerInterface
   */
  protected $layoutPluginManager;
  
  /**
   * {@inheritdoc}
   */
  protected function setUp() : void {
    parent::setUp();
    $this->setUpFilesystem();
    $container = new ContainerBuilder();
    $container->set('string_translation', $this->getStringTranslationStub());
    $this->themeManager = $this->prophesize(ThemeManagerInterface::class);
    $container->set('theme.manager', $this->themeManager
      ->reveal());
    \Drupal::setContainer($container);
    $this->moduleHandler = $this->prophesize(ModuleHandlerInterface::class);
    $this->moduleHandler
      ->moduleExists('module_a')
      ->willReturn(TRUE);
    $this->moduleHandler
      ->moduleExists('theme_a')
      ->willReturn(FALSE);
    $this->moduleHandler
      ->moduleExists('core')
      ->willReturn(FALSE);
    $this->moduleHandler
      ->moduleExists('invalid_provider')
      ->willReturn(FALSE);
    $module_a = new Extension('vfs://root', 'module', 'modules/module_a/module_a.layouts.yml');
    $this->moduleHandler
      ->getModule('module_a')
      ->willReturn($module_a);
    $this->moduleHandler
      ->getModuleDirectories()
      ->willReturn([
      'module_a' => vfsStream::url('root/modules/module_a'),
    ]);
    $this->moduleHandler
      ->alter('layout', Argument::type('array'))
      ->shouldBeCalled();
    $this->themeHandler = $this->prophesize(ThemeHandlerInterface::class);
    $this->themeHandler
      ->themeExists('theme_a')
      ->willReturn(TRUE);
    $this->themeHandler
      ->themeExists('core')
      ->willReturn(FALSE);
    $this->themeHandler
      ->themeExists('invalid_provider')
      ->willReturn(FALSE);
    $theme_a = new Extension('vfs://root', 'theme', 'themes/theme_a/theme_a.layouts.yml');
    $this->themeHandler
      ->getTheme('theme_a')
      ->willReturn($theme_a);
    $this->themeHandler
      ->getThemeDirectories()
      ->willReturn([
      'theme_a' => vfsStream::url('root/themes/theme_a'),
    ]);
    $this->cacheBackend = $this->prophesize(CacheBackendInterface::class);
    $namespaces = new \ArrayObject([
      'Drupal\\Core' => vfsStream::url('root/core/lib/Drupal/Core'),
    ]);
    $class_loader = new ClassLoader();
    $class_loader->addPsr4("Drupal\\Core\\", vfsStream::url("root/core/lib/Drupal/Core"));
    $class_loader->register(TRUE);
    $this->layoutPluginManager = new LayoutPluginManager($namespaces, $this->cacheBackend
      ->reveal(), $this->moduleHandler
      ->reveal(), $this->themeHandler
      ->reveal());
  }
  
  /**
   * @covers ::getDefinitions
   * @covers ::providerExists
   */
  public function testGetDefinitions() : void {
    $expected = [
      'module_a_provided_layout',
      'theme_a_provided_layout',
      'plugin_provided_layout',
      'plugin_provided_by_annotation_layout',
    ];
    $layout_definitions = $this->layoutPluginManager
      ->getDefinitions();
    $this->assertEquals($expected, array_keys($layout_definitions));
    $this->assertContainsOnlyInstancesOf(LayoutDefinition::class, $layout_definitions);
  }
  
  /**
   * @covers ::getDefinition
   * @covers ::processDefinition
   */
  public function testGetDefinition() : void {
    $layout_definition = $this->layoutPluginManager
      ->getDefinition('theme_a_provided_layout');
    $this->assertSame('theme_a_provided_layout', $layout_definition->id());
    $this->assertSame('2 column layout', (string) $layout_definition->getLabel());
    $this->assertSame('Columns: 2', (string) $layout_definition->getCategory());
    $this->assertSame('A theme provided layout', (string) $layout_definition->getDescription());
    $this->assertInstanceOf(TranslatableMarkup::class, $layout_definition->getLabel());
    $this->assertInstanceOf(TranslatableMarkup::class, $layout_definition->getCategory());
    $this->assertInstanceOf(TranslatableMarkup::class, $layout_definition->getDescription());
    $this->assertSame('twocol', $layout_definition->getTemplate());
    $this->assertSame('themes/theme_a/templates', $layout_definition->getPath());
    $this->assertSame('theme_a/twocol', $layout_definition->getLibrary());
    $this->assertSame('twocol', $layout_definition->getThemeHook());
    $this->assertSame('themes/theme_a/templates', $layout_definition->getTemplatePath());
    $this->assertSame('theme_a', $layout_definition->getProvider());
    $this->assertSame('right', $layout_definition->getDefaultRegion());
    $this->assertSame(LayoutDefault::class, $layout_definition->getClass());
    $expected_regions = [
      'left' => [
        'label' => new TranslatableMarkup('Left region', [], [
          'context' => 'layout_region',
        ]),
      ],
      'right' => [
        'label' => new TranslatableMarkup('Right region', [], [
          'context' => 'layout_region',
        ]),
      ],
    ];
    $regions = $layout_definition->getRegions();
    $this->assertEquals($expected_regions, $regions);
    $this->assertInstanceOf(TranslatableMarkup::class, $regions['left']['label']);
    $this->assertInstanceOf(TranslatableMarkup::class, $regions['right']['label']);
    $layout_definition = $this->layoutPluginManager
      ->getDefinition('module_a_provided_layout');
    $this->assertSame('module_a_provided_layout', $layout_definition->id());
    $this->assertSame('1 column layout', (string) $layout_definition->getLabel());
    $this->assertSame('Columns: 1', (string) $layout_definition->getCategory());
    $this->assertSame('A module provided layout', (string) $layout_definition->getDescription());
    $this->assertInstanceOf(TranslatableMarkup::class, $layout_definition->getLabel());
    $this->assertInstanceOf(TranslatableMarkup::class, $layout_definition->getCategory());
    $this->assertInstanceOf(TranslatableMarkup::class, $layout_definition->getDescription());
    $this->assertNull($layout_definition->getTemplate());
    $this->assertSame('modules/module_a/layouts', $layout_definition->getPath());
    $this->assertSame('module_a/onecol', $layout_definition->getLibrary());
    $this->assertSame('onecol', $layout_definition->getThemeHook());
    $this->assertNull($layout_definition->getTemplatePath());
    $this->assertSame('module_a', $layout_definition->getProvider());
    $this->assertSame('top', $layout_definition->getDefaultRegion());
    $this->assertSame(LayoutDefault::class, $layout_definition->getClass());
    $expected_regions = [
      'top' => [
        'label' => new TranslatableMarkup('Top region', [], [
          'context' => 'layout_region',
        ]),
      ],
      'bottom' => [
        'label' => new TranslatableMarkup('Bottom region', [], [
          'context' => 'layout_region',
        ]),
      ],
    ];
    $regions = $layout_definition->getRegions();
    $this->assertEquals($expected_regions, $regions);
    $this->assertInstanceOf(TranslatableMarkup::class, $regions['top']['label']);
    $this->assertInstanceOf(TranslatableMarkup::class, $regions['bottom']['label']);
    // Check that arbitrary property value gets set correctly.
    $this->assertSame('ipsum', $layout_definition->get('lorem'));
    $core_path = '/core/lib/Drupal/Core';
    $layout_definition = $this->layoutPluginManager
      ->getDefinition('plugin_provided_layout');
    $this->assertSame('plugin_provided_layout', $layout_definition->id());
    $this->assertEquals('Layout plugin', $layout_definition->getLabel());
    $this->assertEquals('Columns: 1', $layout_definition->getCategory());
    $this->assertEquals('Test layout', $layout_definition->getDescription());
    $this->assertInstanceOf(TranslatableMarkup::class, $layout_definition->getLabel());
    $this->assertInstanceOf(TranslatableMarkup::class, $layout_definition->getCategory());
    $this->assertInstanceOf(TranslatableMarkup::class, $layout_definition->getDescription());
    $this->assertSame('plugin-provided-layout', $layout_definition->getTemplate());
    $this->assertSame($core_path, $layout_definition->getPath());
    $this->assertNull($layout_definition->getLibrary());
    $this->assertSame('plugin_provided_layout', $layout_definition->getThemeHook());
    $this->assertSame("{$core_path}/templates", $layout_definition->getTemplatePath());
    $this->assertSame('core', $layout_definition->getProvider());
    $this->assertSame('main', $layout_definition->getDefaultRegion());
    $this->assertSame('Drupal\\Core\\Plugin\\Layout\\TestLayout', $layout_definition->getClass());
    $expected_regions = [
      'main' => [
        'label' => new TranslatableMarkup('Main Region', [], [
          'context' => 'layout_region',
        ]),
      ],
    ];
    $regions = $layout_definition->getRegions();
    $this->assertEquals($expected_regions, $regions);
    $this->assertInstanceOf(TranslatableMarkup::class, $regions['main']['label']);
    // Check that arbitrary property value gets set correctly.
    $this->assertSame('adipiscing', $layout_definition->get('consectetur'));
    $layout_definition = $this->layoutPluginManager
      ->getDefinition('plugin_provided_by_annotation_layout');
    $this->assertSame('plugin_provided_by_annotation_layout', $layout_definition->id());
    $this->assertEquals('Layout by annotation plugin', $layout_definition->getLabel());
    $this->assertEquals('Columns: 2', $layout_definition->getCategory());
    $this->assertEquals('Test layout provided by annotated plugin', $layout_definition->getDescription());
    $this->assertInstanceOf(TranslatableMarkup::class, $layout_definition->getLabel());
    $this->assertInstanceOf(TranslatableMarkup::class, $layout_definition->getCategory());
    $this->assertInstanceOf(TranslatableMarkup::class, $layout_definition->getDescription());
    $this->assertSame('plugin-provided-annotation-layout', $layout_definition->getTemplate());
    $this->assertSame($core_path, $layout_definition->getPath());
    $this->assertNull($layout_definition->getLibrary());
    $this->assertSame('plugin_provided_annotation_layout', $layout_definition->getThemeHook());
    $this->assertSame("{$core_path}/templates", $layout_definition->getTemplatePath());
    $this->assertSame('core', $layout_definition->getProvider());
    $this->assertSame('left', $layout_definition->getDefaultRegion());
    $this->assertSame('Drupal\\Core\\Plugin\\Layout\\TestAnnotationLayout', $layout_definition->getClass());
    $expected_regions = [
      'left' => [
        'label' => new TranslatableMarkup('Left Region', [], [
          'context' => 'layout_region',
        ]),
      ],
      'right' => [
        'label' => new TranslatableMarkup('Right Region', [], [
          'context' => 'layout_region',
        ]),
      ],
    ];
    $regions = $layout_definition->getRegions();
    $this->assertEquals($expected_regions, $regions);
    $this->assertInstanceOf(TranslatableMarkup::class, $regions['left']['label']);
    $this->assertInstanceOf(TranslatableMarkup::class, $regions['right']['label']);
  }
  
  /**
   * @covers ::processDefinition
   */
  public function testProcessDefinition() : void {
    $this->moduleHandler
      ->alter('layout', Argument::type('array'))
      ->shouldNotBeCalled();
    $this->expectException(InvalidPluginDefinitionException::class);
    $this->expectExceptionMessage('The "module_a_derived_layout:array_based" layout definition must extend ' . LayoutDefinition::class);
    $module_a_provided_layout = <<<'EOS'
module_a_derived_layout:
  deriver: \Drupal\Tests\Core\Layout\LayoutDeriver
  array_based: true
EOS;
    vfsStream::create([
      'modules' => [
        'module_a' => [
          'module_a.layouts.yml' => $module_a_provided_layout,
        ],
      ],
    ]);
    $this->layoutPluginManager
      ->getDefinitions();
  }
  
  /**
   * @covers ::getThemeImplementations
   */
  public function testGetThemeImplementations() : void {
    $core_path = '/core/lib/Drupal/Core';
    $expected = [
      'layout' => [
        'render element' => 'content',
      ],
      'twocol' => [
        'render element' => 'content',
        'base hook' => 'layout',
        'template' => 'twocol',
        'path' => 'themes/theme_a/templates',
      ],
      'plugin_provided_layout' => [
        'render element' => 'content',
        'base hook' => 'layout',
        'template' => 'plugin-provided-layout',
        'path' => "{$core_path}/templates",
      ],
      'plugin_provided_annotation_layout' => [
        'render element' => 'content',
        'base hook' => 'layout',
        'template' => 'plugin-provided-annotation-layout',
        'path' => "{$core_path}/templates",
      ],
    ];
    $theme_implementations = $this->layoutPluginManager
      ->getThemeImplementations();
    $this->assertEquals($expected, $theme_implementations);
  }
  
  /**
   * @covers ::getCategories
   */
  public function testGetCategories() : void {
    $expected = [
      'Columns: 1',
      'Columns: 2',
    ];
    $categories = $this->layoutPluginManager
      ->getCategories();
    $this->assertEquals($expected, $categories);
  }
  
  /**
   * @covers ::getSortedDefinitions
   */
  public function testGetSortedDefinitions() : void {
    // Sorted by category first, then label.
    $expected = [
      'module_a_provided_layout',
      'plugin_provided_layout',
      'theme_a_provided_layout',
      'plugin_provided_by_annotation_layout',
    ];
    $layout_definitions = $this->layoutPluginManager
      ->getSortedDefinitions();
    $this->assertEquals($expected, array_keys($layout_definitions));
    $this->assertContainsOnlyInstancesOf(LayoutDefinition::class, $layout_definitions);
  }
  
  /**
   * @covers ::getGroupedDefinitions
   */
  public function testGetGroupedDefinitions() : void {
    $category_expected = [
      'Columns: 1' => [
        'module_a_provided_layout',
        'plugin_provided_layout',
      ],
      'Columns: 2' => [
        'theme_a_provided_layout',
        'plugin_provided_by_annotation_layout',
      ],
    ];
    $definitions = $this->layoutPluginManager
      ->getGroupedDefinitions();
    $this->assertEquals(array_keys($category_expected), array_keys($definitions));
    foreach ($category_expected as $category => $expected) {
      $this->assertArrayHasKey($category, $definitions);
      $this->assertEquals($expected, array_keys($definitions[$category]));
      $this->assertContainsOnlyInstancesOf(LayoutDefinition::class, $definitions[$category]);
    }
  }
  
  /**
   * @covers ::getLayoutOptions
   *
   * Test that modules and themes can alter the list of layouts.
   */
  public function testGetLayoutOptions() : void {
    $this->moduleHandler
      ->alter([
      'plugin_filter_layout',
      'plugin_filter_layout__layout',
    ], Argument::type('array'), [], 'layout')
      ->shouldBeCalled();
    $this->themeManager
      ->alter([
      'plugin_filter_layout',
      'plugin_filter_layout__layout',
    ], Argument::type('array'), [], 'layout')
      ->shouldBeCalled();
    $this->layoutPluginManager
      ->getLayoutOptions();
  }
  
  /**
   * Sets up the filesystem with YAML files and annotated plugins.
   */
  protected function setUpFilesystem() {
    $module_a_provided_layout = <<<'EOS'
module_a_provided_layout:
  label: 1 column layout
  category: 'Columns: 1'
  description: 'A module provided layout'
  theme_hook: onecol
  path: layouts
  library: module_a/onecol
  regions:
    top:
      label: Top region
    bottom:
      label: Bottom region
  lorem: ipsum
module_a_derived_layout:
  label: 'Invalid provider derived layout'
  deriver: \Drupal\Tests\Core\Layout\LayoutDeriver
  invalid_provider: true
EOS;
    $theme_a_provided_layout = <<<'EOS'
theme_a_provided_layout:
  class: '\Drupal\Core\Layout\LayoutDefault'
  label: 2 column layout
  category: 'Columns: 2'
  description: 'A theme provided layout'
  template: twocol
  path: templates
  library: theme_a/twocol
  default_region: right
  regions:
    left:
      label: Left region
    right:
      label: Right region
EOS;
    $plugin_provided_layout = <<<'EOS'
<?php
namespace Drupal\Core\Plugin\Layout;
use Drupal\Core\Layout\Attribute\Layout;
use Drupal\Core\Layout\LayoutDefault;
use Drupal\Core\StringTranslation\TranslatableMarkup;
/**
 * The TestLayout Class.
 */
#[Layout(
  id: 'plugin_provided_layout',
  label: new TranslatableMarkup('Layout plugin'),
  category: new TranslatableMarkup('Columns: 1'),
  description: new TranslatableMarkup('Test layout'),
  path: "core/lib/Drupal/Core",
  template: "templates/plugin-provided-layout",
  regions: [
    "main" => [
      "label" => new TranslatableMarkup("Main Region", [], ["context" => "layout_region"]),
    ],
  ],
  consectetur: 'adipiscing',
)]
class TestLayout extends LayoutDefault {}
EOS;
    $plugin_provided_by_annotation_layout = <<<'EOS'
<?php
namespace Drupal\Core\Plugin\Layout;
use Drupal\Core\Layout\LayoutDefault;
/**
 * @Layout(
 *   id = "plugin_provided_by_annotation_layout",
 *   label = @Translation("Layout by annotation plugin"),
 *   category = @Translation("Columns: 2"),
 *   description = @Translation("Test layout provided by annotated plugin"),
 *   path = "core/lib/Drupal/Core",
 *   template = "templates/plugin-provided-annotation-layout",
 *   default_region = "left",
 *   regions = {
 *     "left" = {
 *       "label" = @Translation("Left Region", context = "layout_region")
 *     },
 *     "right" = {
 *        "label" = @Translation("Right Region", context = "layout_region")
 *     }
 *   }
 * )
 */
class TestAnnotationLayout extends LayoutDefault {}
EOS;
    vfsStream::setup('root');
    vfsStream::create([
      'modules' => [
        'module_a' => [
          'module_a.layouts.yml' => $module_a_provided_layout,
        ],
      ],
    ]);
    vfsStream::create([
      'themes' => [
        'theme_a' => [
          'theme_a.layouts.yml' => $theme_a_provided_layout,
        ],
      ],
    ]);
    vfsStream::create([
      'core' => [
        'lib' => [
          'Drupal' => [
            'Core' => [
              'Plugin' => [
                'Layout' => [
                  'TestLayout.php' => $plugin_provided_layout,
                  'TestAnnotationLayout.php' => $plugin_provided_by_annotation_layout,
                ],
              ],
            ],
          ],
        ],
      ],
    ]);
  }

}

Members

Title Sort descending Deprecated Modifiers Object type Summary Overriden Title Overrides
LayoutPluginManagerTest::$cacheBackend protected property Cache backend instance.
LayoutPluginManagerTest::$layoutPluginManager protected property The layout plugin manager.
LayoutPluginManagerTest::$moduleHandler protected property The module handler.
LayoutPluginManagerTest::$themeHandler protected property The theme handler.
LayoutPluginManagerTest::$themeManager protected property The theme manager.
LayoutPluginManagerTest::setUp protected function Overrides UnitTestCase::setUp
LayoutPluginManagerTest::setUpFilesystem protected function Sets up the filesystem with YAML files and annotated plugins.
LayoutPluginManagerTest::testGetCategories public function @covers ::getCategories[[api-linebreak]]
LayoutPluginManagerTest::testGetDefinition public function @covers ::getDefinition[[api-linebreak]]
@covers ::processDefinition[[api-linebreak]]
LayoutPluginManagerTest::testGetDefinitions public function @covers ::getDefinitions[[api-linebreak]]
@covers ::providerExists[[api-linebreak]]
LayoutPluginManagerTest::testGetGroupedDefinitions public function @covers ::getGroupedDefinitions[[api-linebreak]]
LayoutPluginManagerTest::testGetLayoutOptions public function @covers ::getLayoutOptions[[api-linebreak]]
LayoutPluginManagerTest::testGetSortedDefinitions public function @covers ::getSortedDefinitions[[api-linebreak]]
LayoutPluginManagerTest::testGetThemeImplementations public function @covers ::getThemeImplementations[[api-linebreak]]
LayoutPluginManagerTest::testProcessDefinition public function @covers ::processDefinition[[api-linebreak]]
PhpUnitWarnings::$deprecationWarnings private static property Deprecation warnings from PHPUnit to raise with @trigger_error().
PhpUnitWarnings::addWarning public function Converts PHPUnit deprecation warnings to E_USER_DEPRECATED.
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.
RandomGeneratorTrait::randomStringValidate Deprecated public function Callback for random string validation.
UnitTestCase::$root protected property The app root. 1
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::getConfigStorageStub public function Returns a stub config storage that returns the supplied configuration.
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::setUpBeforeClass public static function
UnitTestCase::__get public function

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