Initial Drupal 11 with DDEV setup
This commit is contained in:
@ -0,0 +1,3 @@
|
||||
.some-rule {
|
||||
display: block;
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
name: 'Devel dumper test module'
|
||||
type: module
|
||||
description: 'Test pluggable dumpers.'
|
||||
package: Testing
|
||||
|
||||
# Information added by Drupal.org packaging script on 2025-07-07
|
||||
version: '5.4.0'
|
||||
project: 'devel'
|
||||
datestamp: 1751916160
|
||||
@ -0,0 +1,7 @@
|
||||
devel_dumper_test:
|
||||
version: 0
|
||||
css:
|
||||
theme:
|
||||
css/devel_dumper_test.css: {}
|
||||
js:
|
||||
js/devel_dumper_test.js: {}
|
||||
@ -0,0 +1,40 @@
|
||||
devel_dumper_test.dump:
|
||||
path: '/devel_dumper_test/dump'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel_dumper_test\Controller\DumperTestController::dump'
|
||||
_title: 'Devel Dumper Test'
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel_dumper_test.message:
|
||||
path: '/devel_dumper_test/message'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel_dumper_test\Controller\DumperTestController::message'
|
||||
_title: 'Devel Dumper Test'
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel_dumper_test.export:
|
||||
path: '/devel_dumper_test/export'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel_dumper_test\Controller\DumperTestController::export'
|
||||
_title: 'Devel Dumper Test'
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel_dumper_test.export_renderable:
|
||||
path: '/devel_dumper_test/export_renderable'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel_dumper_test\Controller\DumperTestController::exportRenderable'
|
||||
_title: 'Devel Dumper Test'
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel_dumper_test.debug:
|
||||
path: '/devel_dumper_test/debug'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel_dumper_test\Controller\DumperTestController::debug'
|
||||
_title: 'Devel Dumper Test'
|
||||
requirements:
|
||||
# Specifically give access to all users for testing in testDumpersOutput().
|
||||
_access: 'TRUE'
|
||||
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel_dumper_test\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\devel\DevelDumperManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Controller class for dumper_test module.
|
||||
*
|
||||
* @package Drupal\devel_dumper_test\Controller
|
||||
*/
|
||||
class DumperTestController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The dumper manager.
|
||||
*/
|
||||
protected DevelDumperManagerInterface $dumper;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container): static {
|
||||
$instance = parent::create($container);
|
||||
$instance->dumper = $container->get('devel.dumper');
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dump output to test.
|
||||
*
|
||||
* @return array
|
||||
* The render array output.
|
||||
*/
|
||||
public function dump(): array {
|
||||
$this->dumper->dump('Test output');
|
||||
|
||||
return [
|
||||
'#markup' => 'test',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message output to test.
|
||||
*
|
||||
* @return array
|
||||
* The render array output.
|
||||
*/
|
||||
public function message(): array {
|
||||
$this->dumper->message('Test output');
|
||||
|
||||
return [
|
||||
'#markup' => 'test',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the debug output to test.
|
||||
*
|
||||
* @return array
|
||||
* The render array output.
|
||||
*/
|
||||
public function debug(): array {
|
||||
$this->dumper->debug('Test output');
|
||||
|
||||
return [
|
||||
'#markup' => 'test',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the export output to test.
|
||||
*
|
||||
* @return array
|
||||
* The render array output.
|
||||
*/
|
||||
public function export(): array {
|
||||
return [
|
||||
'#markup' => $this->dumper->export('Test output'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the renderable export output to test.
|
||||
*
|
||||
* @return array
|
||||
* The render array output.
|
||||
*/
|
||||
public function exportRenderable(): array {
|
||||
return $this->dumper->exportAsRenderable('Test output');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel_dumper_test\Plugin\Devel\Dumper;
|
||||
|
||||
use Drupal\Component\Render\MarkupInterface;
|
||||
use Drupal\devel\DevelDumperBase;
|
||||
|
||||
/**
|
||||
* Provides a AvailableTestDumper plugin.
|
||||
*
|
||||
* @DevelDumper(
|
||||
* id = "available_test_dumper",
|
||||
* label = @Translation("Available test dumper."),
|
||||
* description = @Translation("Drupal dumper for testing purposes (available).")
|
||||
* )
|
||||
*/
|
||||
class AvailableTestDumper extends DevelDumperBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump($input, ?string $name = NULL): void {
|
||||
// Add a predetermined string to $input to check if this dumper has been
|
||||
// selected successfully.
|
||||
$input = '<pre>AvailableTestDumper::dump() ' . $input . '</pre>';
|
||||
echo $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function export(mixed $input, ?string $name = NULL): MarkupInterface|string {
|
||||
// Add a predetermined string to $input to check if this dumper has been
|
||||
// selected successfully.
|
||||
$input = '<pre>AvailableTestDumper::export() ' . $input . '</pre>';
|
||||
|
||||
return $this->setSafeMarkup($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exportAsRenderable($input, ?string $name = NULL): array {
|
||||
// Add a predetermined string to $input to check if this dumper has been
|
||||
// selected successfully.
|
||||
$input = '<pre>AvailableTestDumper::exportAsRenderable() ' . $input . '</pre>';
|
||||
|
||||
return [
|
||||
'#attached' => [
|
||||
'library' => ['devel_dumper_test/devel_dumper_test'],
|
||||
],
|
||||
'#markup' => $this->setSafeMarkup($input),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function checkRequirements(): bool {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel_dumper_test\Plugin\Devel\Dumper;
|
||||
|
||||
use Drupal\Component\Render\MarkupInterface;
|
||||
use Drupal\devel\DevelDumperBase;
|
||||
|
||||
/**
|
||||
* Provides a NotAvailableTestDumper plugin.
|
||||
*
|
||||
* @DevelDumper(
|
||||
* id = "not_available_test_dumper",
|
||||
* label = @Translation("Not available test dumper."),
|
||||
* description = @Translation("Drupal dumper for testing purposes (not available).")
|
||||
* )
|
||||
*/
|
||||
class NotAvailableTestDumper extends DevelDumperBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump($input, ?string $name = NULL): void {
|
||||
$input = '<pre>' . $input . '</pre>';
|
||||
echo $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function export(mixed $input, ?string $name = NULL): MarkupInterface|string {
|
||||
$input = '<pre>' . $input . '</pre>';
|
||||
|
||||
return $this->setSafeMarkup($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function checkRequirements(): bool {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
name: 'Devel entity test module'
|
||||
type: module
|
||||
description: 'Provides entity types for Devel tests.'
|
||||
package: Testing
|
||||
dependencies:
|
||||
- drupal:field
|
||||
- drupal:text
|
||||
- drupal:entity_test
|
||||
|
||||
# Information added by Drupal.org packaging script on 2025-07-07
|
||||
version: '5.4.0'
|
||||
project: 'devel'
|
||||
datestamp: 1751916160
|
||||
@ -0,0 +1,2 @@
|
||||
devel_entity_test.local_tasks:
|
||||
deriver: 'Drupal\devel_entity_test\Plugin\Derivative\DevelEntityTestLocalTasks'
|
||||
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test module for the entity API providing several entity types for testing.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_view_mode_info_alter().
|
||||
*/
|
||||
function devel_entity_test_entity_view_mode_info_alter(array &$view_modes): void {
|
||||
$entity_info = \Drupal::entityTypeManager()->getDefinitions();
|
||||
foreach ($entity_info as $entity_type => $info) {
|
||||
if ($info->getProvider() !== 'devel_entity_test_canonical') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($view_modes[$entity_type])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$view_modes[$entity_type] = [
|
||||
'full' => [
|
||||
'label' => t('Full object'),
|
||||
'status' => TRUE,
|
||||
'cache' => TRUE,
|
||||
],
|
||||
'teaser' => [
|
||||
'label' => t('Teaser'),
|
||||
'status' => TRUE,
|
||||
'cache' => TRUE,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel_entity_test\Entity;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
|
||||
/**
|
||||
* Defines the test entity class.
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "devel_entity_test_canonical",
|
||||
* label = @Translation("Devel test entity with canonical link"),
|
||||
* handlers = {
|
||||
* "list_builder" = "Drupal\entity_test\EntityTestListBuilder",
|
||||
* "view_builder" = "Drupal\entity_test\EntityTestViewBuilder",
|
||||
* "access" = "Drupal\entity_test\EntityTestAccessControlHandler",
|
||||
* "form" = {
|
||||
* "default" = "Drupal\entity_test\EntityTestForm",
|
||||
* "delete" = "Drupal\entity_test\EntityTestDeleteForm"
|
||||
* },
|
||||
* "route_provider" = {
|
||||
* "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
|
||||
* },
|
||||
* "translation" = "Drupal\content_translation\ContentTranslationHandler",
|
||||
* "views_data" = "Drupal\entity_test\EntityTestViewsData"
|
||||
* },
|
||||
* base_table = "devel_entity_test_canonical",
|
||||
* persistent_cache = FALSE,
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "uuid" = "uuid",
|
||||
* "bundle" = "type",
|
||||
* "label" = "name",
|
||||
* "langcode" = "langcode",
|
||||
* },
|
||||
* links = {
|
||||
* "canonical" = "/devel_entity_test_canonical/{devel_entity_test_canonical}",
|
||||
* },
|
||||
* field_ui_base_route = "entity.entity_test.admin_form",
|
||||
* )
|
||||
*/
|
||||
class DevelEntityTestCanonical extends EntityTest {
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel_entity_test\Entity;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
|
||||
/**
|
||||
* Defines the test entity class.
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "devel_entity_test_edit",
|
||||
* label = @Translation("Devel test entity with edit form link"),
|
||||
* handlers = {
|
||||
* "list_builder" = "Drupal\entity_test\EntityTestListBuilder",
|
||||
* "view_builder" = "Drupal\entity_test\EntityTestViewBuilder",
|
||||
* "access" = "Drupal\entity_test\EntityTestAccessControlHandler",
|
||||
* "form" = {
|
||||
* "default" = "Drupal\entity_test\EntityTestForm",
|
||||
* "delete" = "Drupal\entity_test\EntityTestDeleteForm"
|
||||
* },
|
||||
* "route_provider" = {
|
||||
* "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
|
||||
* },
|
||||
* "translation" = "Drupal\content_translation\ContentTranslationHandler",
|
||||
* "views_data" = "Drupal\entity_test\EntityTestViewsData"
|
||||
* },
|
||||
* base_table = "devel_entity_test_edit",
|
||||
* persistent_cache = FALSE,
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "uuid" = "uuid",
|
||||
* "bundle" = "type",
|
||||
* "label" = "name",
|
||||
* "langcode" = "langcode",
|
||||
* },
|
||||
* links = {
|
||||
* "edit-form" = "/devel_entity_test_edit/manage/{devel_entity_test_edit}",
|
||||
* },
|
||||
* field_ui_base_route = "entity.entity_test.admin_form",
|
||||
* )
|
||||
*/
|
||||
class DevelEntityTestEdit extends EntityTest {
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel_entity_test\Entity;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
|
||||
/**
|
||||
* Defines the test entity class.
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "devel_entity_test_no_links",
|
||||
* label = @Translation("Devel test entity with no links"),
|
||||
* handlers = {
|
||||
* "list_builder" = "Drupal\entity_test\EntityTestListBuilder",
|
||||
* "view_builder" = "Drupal\entity_test\EntityTestViewBuilder",
|
||||
* "access" = "Drupal\entity_test\EntityTestAccessControlHandler",
|
||||
* "form" = {
|
||||
* "default" = "Drupal\entity_test\EntityTestForm",
|
||||
* "delete" = "Drupal\entity_test\EntityTestDeleteForm"
|
||||
* },
|
||||
* "route_provider" = {
|
||||
* "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
|
||||
* },
|
||||
* "translation" = "Drupal\content_translation\ContentTranslationHandler",
|
||||
* "views_data" = "Drupal\entity_test\EntityTestViewsData"
|
||||
* },
|
||||
* base_table = "devel_entity_test_no_links",
|
||||
* persistent_cache = FALSE,
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "uuid" = "uuid",
|
||||
* "bundle" = "type",
|
||||
* "label" = "name",
|
||||
* "langcode" = "langcode",
|
||||
* },
|
||||
* field_ui_base_route = "entity.entity_test.admin_form",
|
||||
* )
|
||||
*/
|
||||
class DevelEntityTestNoLinks extends EntityTest {
|
||||
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel_entity_test\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
|
||||
/**
|
||||
* Defines the local tasks for all the entity_test entities.
|
||||
*/
|
||||
class DevelEntityTestLocalTasks extends DeriverBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$this->derivatives = [];
|
||||
|
||||
$this->derivatives['devel_entity_test_canonical.canonical'] = [];
|
||||
$this->derivatives['devel_entity_test_canonical.canonical']['base_route'] = "entity.devel_entity_test_canonical.canonical";
|
||||
$this->derivatives['devel_entity_test_canonical.canonical']['route_name'] = "entity.devel_entity_test_canonical.canonical";
|
||||
$this->derivatives['devel_entity_test_canonical.canonical']['title'] = 'View';
|
||||
|
||||
$this->derivatives['devel_entity_test_edit.edit'] = [];
|
||||
$this->derivatives['devel_entity_test_edit.edit']['base_route'] = "entity.devel_entity_test_edit.edit_form";
|
||||
$this->derivatives['devel_entity_test_edit.edit']['route_name'] = "entity.devel_entity_test_edit.edit_form";
|
||||
$this->derivatives['devel_entity_test_edit.edit']['title'] = 'Edit';
|
||||
|
||||
return parent::getDerivativeDefinitions($base_plugin_definition);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
name: 'Devel test module'
|
||||
type: module
|
||||
description: 'Support module for Devel testing.'
|
||||
package: Testing
|
||||
|
||||
# Information added by Drupal.org packaging script on 2025-07-07
|
||||
version: '5.4.0'
|
||||
project: 'devel'
|
||||
datestamp: 1751916160
|
||||
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Helper module for devel test.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_mail().
|
||||
*/
|
||||
function devel_test_mail($key, array &$message, array $params): void {
|
||||
if ($key === 'devel_mail_log') {
|
||||
$message['subject'] = $params['subject'];
|
||||
$message['body'][] = $params['body'];
|
||||
$message['headers']['From'] = $params['headers']['from'];
|
||||
$message['headers'] += $params['headers']['additional'];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
devel.simple_page:
|
||||
path: '/devel/simple-page'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel_test\Controller\DevelTestController::simplePage'
|
||||
_title: 'Simple Page'
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
@ -0,0 +1,7 @@
|
||||
services:
|
||||
|
||||
devel_test.test_route_subscriber:
|
||||
class: Drupal\devel_test\Routing\TestRouteSubscriber
|
||||
arguments: ['@state']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel_test\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Returns responses for devel module routes.
|
||||
*/
|
||||
class DevelTestController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container): static {
|
||||
$instance = parent::create($container);
|
||||
$instance->stringTranslation = $container->get('string_translation');
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a simple page output.
|
||||
*
|
||||
* @return array
|
||||
* A render array.
|
||||
*/
|
||||
public function simplePage(): array {
|
||||
return [
|
||||
'#markup' => $this->t('Simple page'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel_test\Routing;
|
||||
|
||||
use Drupal\Core\Routing\RouteSubscriberBase;
|
||||
use Drupal\Core\State\State;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Router subscriber class for testing purpose.
|
||||
*/
|
||||
class TestRouteSubscriber extends RouteSubscriberBase {
|
||||
|
||||
/**
|
||||
* The state store.
|
||||
*/
|
||||
protected State $state;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*
|
||||
* @param \Drupal\Core\State\State $state
|
||||
* The object State.
|
||||
*/
|
||||
public function __construct(State $state) {
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function alterRoutes(RouteCollection $collection) {
|
||||
$this->state->set('devel_test_route_rebuild', 'Router rebuild fired');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Browser test base class for Devel functional tests.
|
||||
*
|
||||
* DevelCommandsTest should not extend this class so that it can remain
|
||||
* independent and be used as a cut-and-paste example for other developers.
|
||||
*/
|
||||
abstract class DevelBrowserTestBase extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['devel', 'devel_test', 'block'];
|
||||
|
||||
/**
|
||||
* Admin user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* User with Devel acces but not site admin permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $develUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser([
|
||||
'access devel information',
|
||||
'administer site configuration',
|
||||
]);
|
||||
|
||||
$this->develUser = $this->drupalCreateUser([
|
||||
'access devel information',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\devel\Drush\Commands\DevelCommands;
|
||||
use Drush\TestTraits\DrushTestTrait;
|
||||
|
||||
/**
|
||||
* Test class for the Devel drush commands.
|
||||
*
|
||||
* Note: Drush must be installed. Add it to your require-dev in composer.json.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\devel\Drush\Commands\DevelCommands
|
||||
* @group devel
|
||||
*/
|
||||
class DevelCommandsTest extends BrowserTestBase {
|
||||
|
||||
use DrushTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['devel'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Tests drush commands.
|
||||
*/
|
||||
public function testCommands(): void {
|
||||
$this->drush(DevelCommands::TOKEN, [], ['format' => 'json']);
|
||||
$output = $this->getOutputFromJSON();
|
||||
$tokens = array_column($output, 'token');
|
||||
$this->assertContains('account-name', $tokens);
|
||||
|
||||
$this->drush(DevelCommands::SERVICES, [], ['format' => 'json']);
|
||||
$output = $this->getOutputFromJSON();
|
||||
$this->assertContains('current_user', $output);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\devel\Routing\RouteSubscriber;
|
||||
|
||||
/**
|
||||
* Tests container info pages and links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelContainerInfoTest extends DevelBrowserTestBase {
|
||||
|
||||
use DevelWebAssertHelper;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
$this->drupalLogin($this->develUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests container info menu link.
|
||||
*/
|
||||
public function testContainerInfoMenuLink(): void {
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
// Ensures that the events info link is present on the devel menu and that
|
||||
// it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('Container Info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/container/service');
|
||||
$this->assertSession()->pageTextContains('Container services');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests service list page.
|
||||
*/
|
||||
public function testServiceList(): void {
|
||||
$this->drupalGet('/devel/container/service');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Container services');
|
||||
$this->assertContainerInfoLocalTasks();
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the services table is found.
|
||||
$table = $page->find('css', 'table.devel-service-list');
|
||||
$this->assertNotNull($table);
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(4, count($headers));
|
||||
|
||||
$expected_headers = ['ID', 'Class', 'Alias', 'Operations'];
|
||||
$actual_headers = array_map(static fn($element) => $element->getText(), $headers);
|
||||
$this->assertSame($expected_headers, $actual_headers);
|
||||
|
||||
// Ensures that all the services are listed in the table.
|
||||
$cached_definition = \Drupal::service('kernel')->getCachedContainerDefinition();
|
||||
$this->assertNotNull($cached_definition);
|
||||
$rows = $table->findAll('css', 'tbody tr');
|
||||
$this->assertEquals(count($cached_definition['services']), count($rows));
|
||||
|
||||
// Tests the presence of some (arbitrarily chosen) services in the table.
|
||||
$expected_services = [
|
||||
// Alias changed in Drupal 10 so commented out the test for now.
|
||||
// 'config.factory' => [
|
||||
// 'class' => 'Drupal\Core\Config\ConfigFactory',
|
||||
// 'alias' => '',
|
||||
// ],
|
||||
'devel.route_subscriber' => [
|
||||
'class' => RouteSubscriber::class,
|
||||
'alias' => '',
|
||||
],
|
||||
// 'plugin.manager.element_info' => [
|
||||
// 'class' => 'Drupal\Core\Render\ElementInfoManager',
|
||||
// 'alias' => 'element_info',
|
||||
// ],
|
||||
];
|
||||
|
||||
foreach ($expected_services as $service_id => $expected) {
|
||||
$row = $table->find('css', sprintf('tbody tr:contains("%s")', $service_id));
|
||||
$this->assertNotNull($row);
|
||||
|
||||
$cells = $row->findAll('css', 'td');
|
||||
$this->assertEquals(4, count($cells));
|
||||
|
||||
$cell_service_id = $cells[0];
|
||||
$this->assertEquals($service_id, $cell_service_id->getText());
|
||||
$this->assertTrue($cell_service_id->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell_class = $cells[1];
|
||||
$this->assertEquals($expected['class'], $cell_class->getText());
|
||||
$this->assertTrue($cell_class->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell_alias = $cells[2];
|
||||
$this->assertEquals($expected['alias'], $cell_alias->getText());
|
||||
$this->assertTrue($cell_class->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell_operations = $cells[3];
|
||||
$actual_href = $cell_operations->findLink('Devel')->getAttribute('href');
|
||||
$expected_href = Url::fromRoute('devel.container_info.service.detail', ['service_id' => $service_id])->toString();
|
||||
$this->assertEquals($expected_href, $actual_href);
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/container/service');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests service detail page.
|
||||
*/
|
||||
public function testServiceDetail(): void {
|
||||
$service_id = 'devel.dumper';
|
||||
|
||||
// Ensures that the page works as expected.
|
||||
$this->drupalGet('/devel/container/service/' . $service_id);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains(sprintf('Service %s detail', $service_id));
|
||||
|
||||
// Ensures that the page returns a 404 error if the requested service is
|
||||
// not defined.
|
||||
$this->drupalGet('/devel/container/service/not.exists');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/container/service/' . $service_id);
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests parameter list page.
|
||||
*/
|
||||
public function testParameterList(): void {
|
||||
// Ensures that the page works as expected.
|
||||
$this->drupalGet('/devel/container/parameter');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Container parameters');
|
||||
$this->assertContainerInfoLocalTasks();
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the parameters table is found.
|
||||
$table = $page->find('css', 'table.devel-parameter-list');
|
||||
$this->assertNotNull($table);
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(2, count($headers));
|
||||
|
||||
$expected_headers = ['Name', 'Operations'];
|
||||
$actual_headers = array_map(static fn($element) => $element->getText(), $headers);
|
||||
$this->assertSame($expected_headers, $actual_headers);
|
||||
|
||||
// Ensures that all the parameters are listed in the table.
|
||||
$cached_definition = \Drupal::service('kernel')->getCachedContainerDefinition();
|
||||
$this->assertNotNull($cached_definition);
|
||||
$rows = $table->findAll('css', 'tbody tr');
|
||||
$this->assertEquals(count($cached_definition['parameters']), count($rows));
|
||||
|
||||
// Tests the presence of some parameters in the table.
|
||||
$expected_parameters = [
|
||||
'container.modules',
|
||||
'cache_bins',
|
||||
'factory.keyvalue',
|
||||
'twig.config',
|
||||
];
|
||||
|
||||
foreach ($expected_parameters as $parameter_name) {
|
||||
$row = $table->find('css', sprintf('tbody tr:contains("%s")', $parameter_name));
|
||||
$this->assertNotNull($row);
|
||||
|
||||
$cells = $row->findAll('css', 'td');
|
||||
$this->assertEquals(2, count($cells));
|
||||
|
||||
$cell_parameter_name = $cells[0];
|
||||
$this->assertEquals($parameter_name, $cell_parameter_name->getText());
|
||||
$this->assertTrue($cell_parameter_name->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell_operations = $cells[1];
|
||||
$actual_href = $cell_operations->findLink('Devel')->getAttribute('href');
|
||||
$expected_href = Url::fromRoute('devel.container_info.parameter.detail', ['parameter_name' => $parameter_name])->toString();
|
||||
$this->assertEquals($expected_href, $actual_href);
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/container/service');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests parameter detail page.
|
||||
*/
|
||||
public function testParameterDetail(): void {
|
||||
$parameter_name = 'cache_bins';
|
||||
|
||||
// Ensures that the page works as expected.
|
||||
$this->drupalGet('/devel/container/parameter/' . $parameter_name);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains(sprintf('Parameter %s value', $parameter_name));
|
||||
|
||||
// Ensures that the page returns a 404 error if the requested parameter is
|
||||
// not defined.
|
||||
$this->drupalGet('/devel/container/parameter/not_exists');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/container/service/' . $parameter_name);
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that container info local tasks are present.
|
||||
*/
|
||||
protected function assertContainerInfoLocalTasks() {
|
||||
$expected_local_tasks = [
|
||||
['devel.container_info.service', []],
|
||||
['devel.container_info.parameter', []],
|
||||
];
|
||||
|
||||
$this->assertLocalTasks($expected_local_tasks);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\devel_entity_test\Entity\DevelEntityTestCanonical;
|
||||
use Drupal\devel_entity_test\Entity\DevelEntityTestEdit;
|
||||
use Drupal\devel_entity_test\Entity\DevelEntityTestNoLinks;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
|
||||
/**
|
||||
* Tests Devel controller.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelControllerTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $modules = [
|
||||
'devel',
|
||||
'node',
|
||||
'entity_test',
|
||||
'devel_entity_test',
|
||||
'block',
|
||||
];
|
||||
|
||||
/**
|
||||
* Test entity provided by Core.
|
||||
*/
|
||||
protected EntityTest|EntityInterface $entity;
|
||||
|
||||
/**
|
||||
* Devel test entity with canonical link.
|
||||
*/
|
||||
protected DevelEntityTestCanonical|EntityInterface $entityCanonical;
|
||||
|
||||
/**
|
||||
* Devel test entity with edit form link.
|
||||
*/
|
||||
protected DevelEntityTestEdit|EntityInterface $entityEdit;
|
||||
|
||||
/**
|
||||
* Devel test entity with no links.
|
||||
*/
|
||||
protected DevelEntityTestNoLinks|EntityInterface $entityNoLinks;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$entity_type_manager = $this->container->get('entity_type.manager');
|
||||
|
||||
// Create a test entity.
|
||||
$random_label = $this->randomMachineName();
|
||||
$data = ['type' => 'entity_test', 'name' => $random_label];
|
||||
$this->entity = $entity_type_manager->getStorage('entity_test')->create($data);
|
||||
$this->entity->save();
|
||||
|
||||
// Create a test entity with only canonical route.
|
||||
$random_label = $this->randomMachineName();
|
||||
$data = ['type' => 'devel_entity_test_canonical', 'name' => $random_label];
|
||||
$this->entityCanonical = $entity_type_manager->getStorage('devel_entity_test_canonical')->create($data);
|
||||
$this->entityCanonical->save();
|
||||
|
||||
// Create a test entity with only edit route.
|
||||
$random_label = $this->randomMachineName();
|
||||
$data = ['type' => 'devel_entity_test_edit', 'name' => $random_label];
|
||||
$this->entityEdit = $entity_type_manager->getStorage('devel_entity_test_edit')->create($data);
|
||||
$this->entityEdit->save();
|
||||
|
||||
// Create a test entity with no routes.
|
||||
$random_label = $this->randomMachineName();
|
||||
$data = ['type' => 'devel_entity_test_no_links', 'name' => $random_label];
|
||||
$this->entityNoLinks = $entity_type_manager->getStorage('devel_entity_test_no_links')->create($data);
|
||||
$this->entityNoLinks->save();
|
||||
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
|
||||
$web_user = $this->drupalCreateUser([
|
||||
'view test entity',
|
||||
'administer entity_test content',
|
||||
'access devel information',
|
||||
]);
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests route generation.
|
||||
*/
|
||||
public function testRouteGeneration(): void {
|
||||
// Test Devel load and render routes for entities with both route
|
||||
// definitions.
|
||||
$this->drupalGet('entity_test/' . $this->entity->id());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->LinkExists('View');
|
||||
$this->assertSession()->LinkExists('Edit');
|
||||
$this->assertSession()->LinkExists('Devel');
|
||||
$this->drupalGet('devel/entity_test/' . $this->entity->id());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->LinkExists('Definition');
|
||||
$this->assertSession()->LinkExists('Render');
|
||||
$this->assertSession()->LinkExists('Load');
|
||||
$this->assertSession()->LinkExists('Load (with references)');
|
||||
$this->assertSession()->LinkExists('Path alias');
|
||||
$this->assertSession()->linkByHrefExists('devel/render/entity_test/' . $this->entity->id());
|
||||
$this->drupalGet('devel/render/entity_test/' . $this->entity->id());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->linkByHrefExists('devel/definition/entity_test/' . $this->entity->id());
|
||||
$this->drupalGet('devel/definition/entity_test/' . $this->entity->id());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Test Devel load and render routes for entities with only canonical route
|
||||
// definitions.
|
||||
$this->drupalGet('devel_entity_test_canonical/' . $this->entityCanonical->id());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->LinkExists('View');
|
||||
$this->assertSession()->LinkNotExists('Edit');
|
||||
$this->assertSession()->LinkExists('Devel');
|
||||
// Use xpath with equality check on @data-drupal-link-system-path because
|
||||
// assertNoLinkByHref matches on partial values and finds the other link.
|
||||
$this->assertSession()->elementNotExists('xpath',
|
||||
'//a[@data-drupal-link-system-path = "devel/devel_entity_test_canonical/' . $this->entityCanonical->id() . '"]');
|
||||
$this->assertSession()->elementExists('xpath',
|
||||
'//a[@data-drupal-link-system-path = "devel/render/devel_entity_test_canonical/' . $this->entityCanonical->id() . '"]');
|
||||
$this->drupalGet('devel/devel_entity_test_canonical/' . $this->entityCanonical->id());
|
||||
// This url used to be '404 not found', but is now '200 OK' following the
|
||||
// generating of devel load links for all entity types.
|
||||
// @see https://gitlab.com/drupalspoons/devel/-/issues/377
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->drupalGet('devel/render/devel_entity_test_canonical/' . $this->entityCanonical->id());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->LinkExists('Definition');
|
||||
$this->assertSession()->LinkExists('Render');
|
||||
$this->assertSession()->LinkNotExists('Load');
|
||||
$this->assertSession()->LinkNotExists('Load (with references)');
|
||||
$this->assertSession()->LinkExists('Path alias');
|
||||
$this->assertSession()->linkByHrefExists('devel/definition/devel_entity_test_canonical/' . $this->entityCanonical->id());
|
||||
$this->drupalGet('devel/definition/devel_entity_test_canonical/' . $this->entityCanonical->id());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Test Devel load and render routes for entities with only edit route
|
||||
// definitions.
|
||||
$this->drupalGet('devel_entity_test_edit/manage/' . $this->entityEdit->id());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->LinkNotExists('View');
|
||||
$this->assertSession()->LinkExists('Edit');
|
||||
$this->assertSession()->LinkExists('Devel');
|
||||
$this->assertSession()->linkByHrefExists('devel/devel_entity_test_edit/' . $this->entityEdit->id());
|
||||
$this->drupalGet('devel/devel_entity_test_edit/' . $this->entityEdit->id());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->LinkExists('Definition');
|
||||
$this->assertSession()->LinkNotExists('Render');
|
||||
$this->assertSession()->LinkExists('Load');
|
||||
$this->assertSession()->LinkExists('Load (with references)');
|
||||
$this->assertSession()->LinkExists('Path alias');
|
||||
$this->assertSession()->linkByHrefExists('devel/definition/devel_entity_test_edit/' . $this->entityEdit->id());
|
||||
$this->assertSession()->linkByHrefNotExists('devel/render/devel_entity_test_edit/' . $this->entityEdit->id());
|
||||
$this->drupalGet('devel/definition/devel_entity_test_edit/' . $this->entityEdit->id());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->drupalGet('devel/render/devel_entity_test_edit/' . $this->entityEdit->id());
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Test Devel load and render routes for entities with no route
|
||||
// definitions.
|
||||
$this->drupalGet('devel_entity_test_no_links/' . $this->entityEdit->id());
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
$this->drupalGet('devel/devel_entity_test_no_links/' . $this->entityNoLinks->id());
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->drupalGet('devel/render/devel_entity_test_no_links/' . $this->entityNoLinks->id());
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
$this->drupalGet('devel/definition/devel_entity_test_no_links/' . $this->entityNoLinks->id());
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the field info page.
|
||||
*/
|
||||
public function testFieldInfoPage(): void {
|
||||
$this->drupalGet('/devel/field/info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Field types');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
/**
|
||||
* Tests pluggable dumper feature.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelDumperTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $modules = ['devel', 'devel_dumper_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test dumpers configuration page.
|
||||
*/
|
||||
public function testDumpersConfiguration(): void {
|
||||
$this->drupalGet('admin/config/development/devel');
|
||||
|
||||
// Ensures that the dumper input is present on the config page.
|
||||
$this->assertSession()->fieldExists('dumper');
|
||||
|
||||
// No need to ensure that the 'default' dumper is enabled by default via
|
||||
// "checkboxChecked('edit-dumper-default')" since devel_install does dynamic
|
||||
// default.
|
||||
// Ensures that all dumpers (both those declared by devel and by other
|
||||
// modules) are present on the config page and that only the available
|
||||
// dumpers are selectable.
|
||||
$dumpers = [
|
||||
'var_dumper' => 'Symfony var-dumper',
|
||||
'available_test_dumper' => 'Available test dumper',
|
||||
'not_available_test_dumper' => 'Not available test dumper',
|
||||
];
|
||||
$available_dumpers = ['default', 'var_dumper', 'available_test_dumper'];
|
||||
|
||||
foreach ($dumpers as $dumper => $label) {
|
||||
// Check that a radio option exists for the specified dumper.
|
||||
$this->assertSession()->elementExists('xpath', '//input[@type="radio" and @name="dumper" and @value="' . $dumper . '"]');
|
||||
$this->assertSession()->pageTextContains($label);
|
||||
|
||||
// Check that the available dumpers are enabled and the non-available
|
||||
// dumpers are not enabled.
|
||||
if (in_array($dumper, $available_dumpers)) {
|
||||
$this->assertSession()->elementExists('xpath', '//input[@name="dumper" and not(@disabled="disabled") and @value="' . $dumper . '"]');
|
||||
}
|
||||
else {
|
||||
$this->assertSession()->elementExists('xpath', '//input[@name="dumper" and @disabled="disabled" and @value="' . $dumper . '"]');
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures that saving of the dumpers configuration works as expected.
|
||||
$edit = [
|
||||
'dumper' => 'var_dumper',
|
||||
];
|
||||
$this->submitForm($edit, 'Save configuration');
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
$this->assertSession()->checkboxChecked('Symfony var-dumper');
|
||||
|
||||
$config = \Drupal::config('devel.settings')->get('devel_dumper');
|
||||
$this->assertEquals('var_dumper', $config, 'The configuration options have been properly saved');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test variable is dumped in page.
|
||||
*/
|
||||
public function testDumpersOutput(): void {
|
||||
$edit = [
|
||||
'dumper' => 'available_test_dumper',
|
||||
];
|
||||
$this->drupalGet('admin/config/development/devel');
|
||||
$this->submitForm($edit, 'Save configuration');
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
|
||||
$this->drupalGet('devel_dumper_test/dump');
|
||||
$elements = $this->xpath('//body/pre[contains(text(), :message)]', [':message' => 'AvailableTestDumper::dump() Test output']);
|
||||
$this->assertNotEmpty($elements, 'Dumped message #1 is present.');
|
||||
|
||||
$this->drupalGet('devel_dumper_test/message');
|
||||
$elements = $this->xpath('//div[@aria-label="Status message"]/pre[contains(text(), :message)]', [':message' => 'AvailableTestDumper::export() Test output']);
|
||||
$this->assertNotEmpty($elements, 'Dumped message #2 is present.');
|
||||
|
||||
$this->drupalGet('devel_dumper_test/export');
|
||||
$elements = $this->xpath('//div[@class="layout-content"]//pre[contains(text(), :message)]', [':message' => 'AvailableTestDumper::export() Test output']);
|
||||
$this->assertNotEmpty($elements, 'Dumped message #3 is present.');
|
||||
|
||||
$this->drupalGet('devel_dumper_test/export_renderable');
|
||||
$elements = $this->xpath('//div[@class="layout-content"]//pre[contains(text(), :message)]', [':message' => 'AvailableTestDumper::exportAsRenderable() Test output']);
|
||||
$this->assertNotEmpty($elements, 'Dumped message #4 is present.');
|
||||
// Ensures that plugins can add libraries to the page when the
|
||||
// ::exportAsRenderable() method is used.
|
||||
$this->assertSession()->responseContains('devel_dumper_test/css/devel_dumper_test.css');
|
||||
$this->assertSession()->responseContains('devel_dumper_test/js/devel_dumper_test.js');
|
||||
|
||||
$debug_filename = \Drupal::service('file_system')->getTempDirectory() . '/drupal_debug.txt';
|
||||
$this->drupalGet('devel_dumper_test/debug');
|
||||
$file_content = file_get_contents($debug_filename);
|
||||
$expected = <<<EOF
|
||||
<pre>AvailableTestDumper::export() Test output</pre>
|
||||
|
||||
EOF;
|
||||
$this->assertEquals($file_content, $expected, 'Dumped message #5 is present.');
|
||||
|
||||
// Ensures that the DevelDumperManager::debug() is not access checked and
|
||||
// that the dump is written in the debug file even if the user has not the
|
||||
// 'access devel information' permission.
|
||||
file_put_contents($debug_filename, '');
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel_dumper_test/debug');
|
||||
$file_content = file_get_contents($debug_filename);
|
||||
$expected = <<<EOF
|
||||
<pre>AvailableTestDumper::export() Test output</pre>
|
||||
|
||||
EOF;
|
||||
$this->assertEquals($file_content, $expected, 'Dumped message #6 is present.');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Behat\Mink\Element\NodeElement;
|
||||
use Drupal\Core\Render\Element\Button;
|
||||
use Drupal\Core\Render\Element\Form;
|
||||
use Drupal\Core\Render\Element\Html;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Tests element info pages and links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelElementInfoTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
$this->drupalLogin($this->develUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests element info menu link.
|
||||
*/
|
||||
public function testElementInfoMenuLink(): void {
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
// Ensures that the element info link is present on the devel menu and that
|
||||
// it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('Element Info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/elements');
|
||||
$this->assertSession()->pageTextContains('Element Info');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests element list page.
|
||||
*/
|
||||
public function testElementList(): void {
|
||||
$this->drupalGet('/devel/elements');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Element Info');
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the element list table is found.
|
||||
$table = $page->find('css', 'table.devel-element-list');
|
||||
$this->assertNotNull($table);
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(4, count($headers));
|
||||
|
||||
$expected_headers = ['Name', 'Provider', 'Class', 'Operations'];
|
||||
$actual_headers = array_map(static fn(NodeElement $element) => $element->getText(), $headers);
|
||||
$this->assertSame($expected_headers, $actual_headers);
|
||||
|
||||
// Tests the presence of some (arbitrarily chosen) elements in the table.
|
||||
$expected_elements = [
|
||||
'button' => [
|
||||
'class' => Button::class,
|
||||
'provider' => 'core',
|
||||
],
|
||||
'form' => [
|
||||
'class' => Form::class,
|
||||
'provider' => 'core',
|
||||
],
|
||||
'html' => [
|
||||
'class' => Html::class,
|
||||
'provider' => 'core',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($expected_elements as $element_name => $element) {
|
||||
$row = $table->find('css', sprintf('tbody tr:contains("%s")', $element_name));
|
||||
$this->assertNotNull($row);
|
||||
|
||||
$cells = $row->findAll('css', 'td');
|
||||
$this->assertEquals(4, count($cells));
|
||||
|
||||
$cell = $cells[0];
|
||||
$this->assertEquals($element_name, $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[1];
|
||||
$this->assertEquals($element['provider'], $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[2];
|
||||
$this->assertEquals($element['class'], $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[3];
|
||||
$actual_href = $cell->findLink('Devel')->getAttribute('href');
|
||||
$expected_href = Url::fromRoute('devel.elements_page.detail', ['element_name' => $element_name])->toString();
|
||||
$this->assertEquals($expected_href, $actual_href);
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible only to the users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/elements');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests element detail page.
|
||||
*/
|
||||
public function testElementDetail(): void {
|
||||
$element_name = 'button';
|
||||
|
||||
// Ensures that the page works as expected.
|
||||
$this->drupalGet('/devel/elements/' . $element_name);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Element ' . $element_name);
|
||||
|
||||
// Ensures that the page returns a 404 error if the requested element is
|
||||
// not defined.
|
||||
$this->drupalGet('/devel/elements/not_exists');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('/devel/elements/' . $element_name);
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Behat\Mink\Element\NodeElement;
|
||||
use Drupal\Core\Datetime\Entity\DateFormat;
|
||||
use Drupal\Core\Entity\Entity\EntityViewMode;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Tests entity type info pages and links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelEntityTypeInfoTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
$this->drupalLogin($this->develUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity info menu link.
|
||||
*/
|
||||
public function testEntityInfoMenuLink(): void {
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
// Ensures that the entity type info link is present on the devel menu and
|
||||
// that it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('Entity Info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/entity/info');
|
||||
$this->assertSession()->pageTextContains('Entity Info');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity type list page.
|
||||
*/
|
||||
public function testEntityTypeList(): void {
|
||||
$this->drupalGet('/devel/entity/info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Entity Info');
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the entity type list table is found.
|
||||
$table = $page->find('css', 'table.devel-entity-type-list');
|
||||
$this->assertNotNull($table);
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(5, count($headers));
|
||||
|
||||
$expected_headers = ['ID', 'Name', 'Provider', 'Class', 'Operations'];
|
||||
$actual_headers = array_map(static fn(NodeElement $element) => $element->getText(), $headers);
|
||||
$this->assertSame($expected_headers, $actual_headers);
|
||||
|
||||
// Tests the presence of some (arbitrarily chosen) entity types in the
|
||||
// table.
|
||||
$expected_types = [
|
||||
'date_format' => [
|
||||
'name' => 'Date format',
|
||||
'class' => DateFormat::class,
|
||||
'provider' => 'core',
|
||||
],
|
||||
'block' => [
|
||||
'name' => 'Block',
|
||||
'class' => 'Drupal\block\Entity\Block',
|
||||
'provider' => 'block',
|
||||
],
|
||||
'entity_view_mode' => [
|
||||
'name' => 'View mode',
|
||||
'class' => EntityViewMode::class,
|
||||
'provider' => 'core',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($expected_types as $entity_type_id => $entity_type) {
|
||||
$row = $table->find('css', sprintf('tbody tr:contains("%s")', $entity_type_id));
|
||||
$this->assertNotNull($row);
|
||||
|
||||
$cells = $row->findAll('css', 'td');
|
||||
$this->assertEquals(5, count($cells));
|
||||
|
||||
$cell = $cells[0];
|
||||
$this->assertEquals($entity_type_id, $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[1];
|
||||
$this->assertEquals($entity_type['name'], $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[2];
|
||||
$this->assertEquals($entity_type['provider'], $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[3];
|
||||
$this->assertEquals($entity_type['class'], $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[4];
|
||||
$actual_href = $cell->findLink('Devel')->getAttribute('href');
|
||||
$expected_href = Url::fromRoute('devel.entity_info_page.detail', ['entity_type_id' => $entity_type_id])->toString();
|
||||
$this->assertEquals($expected_href, $actual_href);
|
||||
|
||||
$actual_href = $cell->findLink('Fields')->getAttribute('href');
|
||||
$expected_href = Url::fromRoute('devel.entity_info_page.fields', ['entity_type_id' => $entity_type_id])->toString();
|
||||
$this->assertEquals($expected_href, $actual_href);
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible only to the users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/entity/info');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity type detail page.
|
||||
*/
|
||||
public function testEntityTypeDetail(): void {
|
||||
$entity_type_id = 'date_format';
|
||||
|
||||
// Ensures that the page works as expected.
|
||||
$this->drupalGet('/devel/entity/info/' . $entity_type_id);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Entity type ' . $entity_type_id);
|
||||
|
||||
// Ensures that the page returns a 404 error if the requested entity type is
|
||||
// not defined.
|
||||
$this->drupalGet('/devel/entity/info/not_exists');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('/devel/entity/info/' . $entity_type_id);
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity type fields page.
|
||||
*/
|
||||
public function testEntityTypeFields(): void {
|
||||
$entity_type_id = 'date_format';
|
||||
|
||||
// Ensures that the page works as expected.
|
||||
$this->drupalGet('/devel/entity/fields/' . $entity_type_id);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Entity fields ' . $entity_type_id);
|
||||
|
||||
// Ensures that the page returns a 404 error if the requested entity type is
|
||||
// not defined.
|
||||
$this->drupalGet('/devel/entity/fields/not_exists');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('/devel/entity/fields/' . $entity_type_id);
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
/**
|
||||
* Tests devel error handler.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelErrorHandlerTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* Tests devel error handler.
|
||||
*/
|
||||
public function testErrorHandler(): void {
|
||||
$messages_selector = '[data-drupal-messages]';
|
||||
|
||||
$expected_notice = 'This is an example notice';
|
||||
$expected_warning = 'This is an example warning';
|
||||
|
||||
$config = $this->config('system.logging');
|
||||
$config->set('error_level', ERROR_REPORTING_DISPLAY_VERBOSE)->save();
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Ensures that the error handler config is present on the config page and
|
||||
// by default the standard error handler is selected.
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEquals($error_handlers, [DEVEL_ERROR_HANDLER_STANDARD => DEVEL_ERROR_HANDLER_STANDARD]);
|
||||
$this->drupalGet('admin/config/development/devel');
|
||||
$this->assertTrue($this->assertSession()->optionExists('edit-error-handlers', (string) DEVEL_ERROR_HANDLER_STANDARD)->hasAttribute('selected'));
|
||||
|
||||
// Ensures that selecting the DEVEL_ERROR_HANDLER_NONE option no error
|
||||
// (raw or message) is shown on the site in case of php errors.
|
||||
$edit = [
|
||||
'error_handlers[]' => DEVEL_ERROR_HANDLER_NONE,
|
||||
];
|
||||
$this->submitForm($edit, 'Save configuration');
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEquals($error_handlers, [DEVEL_ERROR_HANDLER_NONE => DEVEL_ERROR_HANDLER_NONE]);
|
||||
$this->assertTrue($this->assertSession()->optionExists('edit-error-handlers', (string) DEVEL_ERROR_HANDLER_NONE)->hasAttribute('selected'));
|
||||
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
// @todo Two assertions commented out. Can be fixed in conjunction with the following two issues.
|
||||
// @see https://gitlab.com/drupalspoons/devel/-/issues/420
|
||||
// @see https://gitlab.com/drupalspoons/devel/-/issues/454
|
||||
// $this->assertSession()->pageTextNotContains($expected_notice);
|
||||
// $this->assertSession()->pageTextNotContains($expected_warning);
|
||||
$this->assertSession()->elementNotExists('css', $messages_selector);
|
||||
|
||||
// Ensures that selecting the DEVEL_ERROR_HANDLER_BACKTRACE_KINT option a
|
||||
// backtrace above the rendered page is shown on the site in case of php
|
||||
// errors.
|
||||
$edit = [
|
||||
'error_handlers[]' => DEVEL_ERROR_HANDLER_BACKTRACE_KINT,
|
||||
];
|
||||
$this->submitForm($edit, 'Save configuration');
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEquals($error_handlers, [DEVEL_ERROR_HANDLER_BACKTRACE_KINT => DEVEL_ERROR_HANDLER_BACKTRACE_KINT]);
|
||||
$this->assertTrue($this->assertSession()->optionExists('edit-error-handlers', (string) DEVEL_ERROR_HANDLER_BACKTRACE_KINT)->hasAttribute('selected'));
|
||||
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->elementNotExists('css', $messages_selector);
|
||||
|
||||
// Ensures that selecting the DEVEL_ERROR_HANDLER_BACKTRACE_DPM option a
|
||||
// backtrace in the message area is shown on the site in case of php errors.
|
||||
$edit = [
|
||||
'error_handlers[]' => DEVEL_ERROR_HANDLER_BACKTRACE_DPM,
|
||||
];
|
||||
$this->submitForm($edit, 'Save configuration');
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEquals($error_handlers, [DEVEL_ERROR_HANDLER_BACKTRACE_DPM => DEVEL_ERROR_HANDLER_BACKTRACE_DPM]);
|
||||
$this->assertTrue($this->assertSession()->optionExists('edit-error-handlers', (string) DEVEL_ERROR_HANDLER_BACKTRACE_DPM)->hasAttribute('selected'));
|
||||
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_notice);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_warning);
|
||||
|
||||
// Ensures that when multiple handlers are selected, the output produced by
|
||||
// every handler is shown on the site in case of php errors.
|
||||
$edit = [
|
||||
'error_handlers[]' => [
|
||||
DEVEL_ERROR_HANDLER_BACKTRACE_KINT => DEVEL_ERROR_HANDLER_BACKTRACE_KINT,
|
||||
DEVEL_ERROR_HANDLER_BACKTRACE_DPM => DEVEL_ERROR_HANDLER_BACKTRACE_DPM,
|
||||
],
|
||||
];
|
||||
$this->submitForm($edit, 'Save configuration');
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEquals($error_handlers, [
|
||||
DEVEL_ERROR_HANDLER_BACKTRACE_KINT => DEVEL_ERROR_HANDLER_BACKTRACE_KINT,
|
||||
DEVEL_ERROR_HANDLER_BACKTRACE_DPM => DEVEL_ERROR_HANDLER_BACKTRACE_DPM,
|
||||
]);
|
||||
$this->assertTrue($this->assertSession()->optionExists('edit-error-handlers', (string) DEVEL_ERROR_HANDLER_BACKTRACE_KINT)->hasAttribute('selected'));
|
||||
$this->assertTrue($this->assertSession()->optionExists('edit-error-handlers', (string) DEVEL_ERROR_HANDLER_BACKTRACE_DPM)->hasAttribute('selected'));
|
||||
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_notice);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_warning);
|
||||
|
||||
// Ensures that setting the error reporting to all the output produced by
|
||||
// handlers is shown on the site in case of php errors.
|
||||
$config->set('error_level', ERROR_REPORTING_DISPLAY_ALL)->save();
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_notice);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_warning);
|
||||
|
||||
// Ensures that setting the error reporting to some the output produced by
|
||||
// handlers is shown on the site in case of php errors.
|
||||
$config->set('error_level', ERROR_REPORTING_DISPLAY_SOME)->save();
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_notice);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_warning);
|
||||
|
||||
// Ensures that setting the error reporting to none the output produced by
|
||||
// handlers is not shown on the site in case of php errors.
|
||||
$config->set('error_level', ERROR_REPORTING_HIDE)->save();
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextNotContains($expected_notice);
|
||||
$this->assertSession()->pageTextNotContains($expected_warning);
|
||||
$this->assertSession()->elementNotExists('css', $messages_selector);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
/**
|
||||
* Tests event info pages and links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelEventInfoTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
$this->drupalLogin($this->develUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests event info menu link.
|
||||
*/
|
||||
public function testEventsInfoMenuLink(): void {
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
// Ensures that the events info link is present on the devel menu and that
|
||||
// it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('Events Info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/events');
|
||||
$this->assertSession()->pageTextContains('Events');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests event info page.
|
||||
*/
|
||||
public function testEventList(): void {
|
||||
/** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher */
|
||||
$event_dispatcher = $this->container->get('event_dispatcher');
|
||||
|
||||
$this->drupalGet('/devel/events');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Events');
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the event table is found.
|
||||
$table = $page->find('css', 'table.devel-event-list');
|
||||
$this->assertNotNull($table);
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(3, count($headers));
|
||||
|
||||
$expected_headers = ['Event Name', 'Callable', 'Priority'];
|
||||
$actual_headers = array_map(static fn($element) => $element->getText(), $headers);
|
||||
$this->assertSame($expected_headers, $actual_headers);
|
||||
|
||||
// Ensures that all the events are listed in the table.
|
||||
$events = $event_dispatcher->getListeners();
|
||||
$event_header_row = $table->findAll('css', 'tbody tr th.devel-event-name-header');
|
||||
$this->assertEquals(count($events), count($event_header_row));
|
||||
|
||||
// Tests the presence of some (arbitrarily chosen) events and related
|
||||
// listeners in the table. The event items are tested dynamically so no
|
||||
// test failures are expected if listeners change.
|
||||
$expected_events = [
|
||||
'config.delete',
|
||||
'kernel.request',
|
||||
'routing.route_alter',
|
||||
];
|
||||
|
||||
foreach ($expected_events as $event_name) {
|
||||
// Ensures that the event header is present in the table.
|
||||
$event_header_row = $table->findAll('css', sprintf('tbody tr th:contains("%s")', $event_name));
|
||||
$this->assertTrue(count($event_header_row) >= 1);
|
||||
|
||||
// Ensures that all the event listener are listed in the table.
|
||||
$event_rows = $table->findAll('css', sprintf('tbody tr:contains("%s")', $event_name));
|
||||
// Remove the header row.
|
||||
array_shift($event_rows);
|
||||
$listeners = $event_dispatcher->getListeners($event_name);
|
||||
foreach ($listeners as $index => $listener) {
|
||||
$cells = $event_rows[$index]->findAll('css', 'td');
|
||||
$this->assertEquals(3, count($cells));
|
||||
|
||||
$cell_event_name = $cells[0];
|
||||
$this->assertEquals($event_name, $cell_event_name->getText());
|
||||
$this->assertTrue($cell_event_name->hasClass('table-filter-text-source'));
|
||||
$this->assertTrue($cell_event_name->hasClass('visually-hidden'));
|
||||
|
||||
$cell_callable = $cells[1];
|
||||
is_callable($listener, TRUE, $callable_name);
|
||||
$this->assertEquals($callable_name, $cell_callable->getText());
|
||||
|
||||
$cell_methods = $cells[2];
|
||||
$priority = $event_dispatcher->getListenerPriority($event_name, $listener);
|
||||
$this->assertEquals($priority, $cell_methods->getText());
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible only to the users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/events');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
/**
|
||||
* Tests layout info pages and links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelLayoutInfoTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['devel', 'block', 'layout_discovery'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
$this->drupalLogin($this->develUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests layout info menu link.
|
||||
*/
|
||||
public function testLayoutsInfoMenuLink(): void {
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
// Ensures that the layout info link is present on the devel menu and that
|
||||
// it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('Layouts Info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/layouts');
|
||||
$this->assertSession()->pageTextContains('Layout');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests layout info page.
|
||||
*/
|
||||
public function testLayoutList(): void {
|
||||
$this->drupalGet('/devel/layouts');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Layouts');
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the layout table is found.
|
||||
$table = $page->find('css', 'table.devel-layout-list');
|
||||
$this->assertNotNull($table);
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(6, count($headers));
|
||||
|
||||
$expected_headers = [
|
||||
'Icon',
|
||||
'Label',
|
||||
'Description',
|
||||
'Category',
|
||||
'Regions',
|
||||
'Provider',
|
||||
];
|
||||
$actual_headers = array_map(static fn($element) => $element->getText(), $headers);
|
||||
$this->assertSame($expected_headers, $actual_headers);
|
||||
|
||||
// Ensures that all the layouts are listed in the table.
|
||||
/** @var \Drupal\Core\Layout\LayoutPluginManagerInterface $layout_manager */
|
||||
$layout_manager = $this->container->get('plugin.manager.core.layout');
|
||||
$layouts = $layout_manager->getDefinitions();
|
||||
$table_rows = $table->findAll('css', 'tbody tr');
|
||||
$this->assertEquals(count($layouts), count($table_rows));
|
||||
|
||||
$index = 0;
|
||||
foreach ($layouts as $layout) {
|
||||
$cells = $table_rows[$index]->findAll('css', 'td');
|
||||
$this->assertEquals(6, count($cells));
|
||||
|
||||
$cell_layout_icon = $cells[0];
|
||||
$icon_path = $layout->getIconPath();
|
||||
if ($icon_path === NULL || $icon_path === '') {
|
||||
// @todo test that the icon path image is set correctly
|
||||
}
|
||||
else {
|
||||
$cell_layout_icon_text = $cell_layout_icon->getText();
|
||||
$this->assertTrue($cell_layout_icon_text === '');
|
||||
}
|
||||
|
||||
$cell_layout_label = $cells[1];
|
||||
$this->assertEquals($cell_layout_label->getText(), $layout->getLabel());
|
||||
|
||||
$cell_layout_description = $cells[2];
|
||||
$this->assertEquals($cell_layout_description->getText(), $layout->getDescription());
|
||||
|
||||
$cell_layout_category = $cells[3];
|
||||
$this->assertEquals($cell_layout_category->getText(), $layout->getCategory());
|
||||
|
||||
$cell_layout_regions = $cells[4];
|
||||
$this->assertEquals($cell_layout_regions->getText(), implode(', ', $layout->getRegionLabels()));
|
||||
|
||||
$cell_layout_provider = $cells[5];
|
||||
$this->assertEquals($cell_layout_provider->getText(), $layout->getProvider());
|
||||
|
||||
++$index;
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible only to the users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/layouts');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the dependency with layout_discovery module.
|
||||
*/
|
||||
public function testLayoutDiscoveryDependency(): void {
|
||||
$this->container->get('module_installer')->uninstall(['layout_discovery']);
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
|
||||
// Ensures that the layout info link is not present on the devel menu.
|
||||
$this->drupalGet('');
|
||||
$this->assertSession()->linkNotExists('Layouts Info');
|
||||
|
||||
// Ensures that the layouts info page is not available.
|
||||
$this->drupalGet('/devel/layouts');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Check a few other devel pages to verify devel module stil works.
|
||||
$this->drupalGet('/devel/events');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->drupalGet('devel/routes');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->drupalGet('/devel/container/service');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Tests devel menu links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelMenuLinksTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
// Devel links currently appears only in the devel menu.
|
||||
// Place the devel menu block so we can ensure that these link works
|
||||
// properly.
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests CSFR protected links.
|
||||
*/
|
||||
public function testCsrfProtectedLinks(): void {
|
||||
// Ensure CSRF link are not accessible directly.
|
||||
$this->drupalGet('devel/run-cron');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
$this->drupalGet('devel/cache/clear');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Ensure clear cache link works properly.
|
||||
$this->assertSession()->linkExists('Cache clear');
|
||||
$this->clickLink('Cache clear');
|
||||
$this->assertSession()->pageTextContains('Cache cleared.');
|
||||
|
||||
// Ensure run cron link works properly.
|
||||
$this->assertSession()->linkExists('Run cron');
|
||||
$this->clickLink('Run cron');
|
||||
$this->assertSession()->pageTextContains('Cron ran successfully.');
|
||||
|
||||
// Ensure CSRF protected links work properly after change session.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->assertSession()->linkExists('Cache clear');
|
||||
$this->clickLink('Cache clear');
|
||||
$this->assertSession()->pageTextContains('Cache cleared.');
|
||||
|
||||
$this->assertSession()->linkExists('Run cron');
|
||||
$this->clickLink('Run cron');
|
||||
$this->assertSession()->pageTextContains('Cron ran successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests redirect destination links.
|
||||
*/
|
||||
public function testRedirectDestinationLinks(): void {
|
||||
// By default, in the testing profile, front page is the user canonical URI.
|
||||
// For better testing do not use the default frontpage.
|
||||
$url = Url::fromRoute('devel.simple_page');
|
||||
|
||||
$this->drupalGet($url);
|
||||
$this->assertSession()->linkExists('Reinstall Modules');
|
||||
$this->clickLink('Reinstall Modules');
|
||||
$this->assertSession()->addressEquals('devel/reinstall');
|
||||
|
||||
$this->drupalGet($url);
|
||||
$this->assertSession()->linkExists('Rebuild Menu');
|
||||
$this->clickLink('Rebuild Menu');
|
||||
$this->assertSession()->addressEquals('devel/menu/reset');
|
||||
|
||||
$this->drupalGet($url);
|
||||
$this->assertSession()->linkExists('Cache clear');
|
||||
$this->clickLink('Cache clear');
|
||||
$this->assertSession()->pageTextContains('Cache cleared.');
|
||||
$this->assertSession()->addressEquals($url->toString());
|
||||
|
||||
$this->drupalGet($url);
|
||||
$this->assertSession()->linkExists('Run cron');
|
||||
$this->clickLink('Run cron');
|
||||
$this->assertSession()->pageTextContains('Cron ran successfully.');
|
||||
$this->assertSession()->addressEquals($url->toString());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
/**
|
||||
* Tests reinstall modules.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelModulesReinstallTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'minimal';
|
||||
|
||||
/**
|
||||
* Set up test.
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinstall modules.
|
||||
*/
|
||||
public function testDevelReinstallModules(): void {
|
||||
// Minimal profile enables only dblog, block and node.
|
||||
$modules = ['dblog', 'block'];
|
||||
|
||||
// Needed for compare correctly the message.
|
||||
sort($modules);
|
||||
|
||||
$this->drupalGet('devel/reinstall');
|
||||
|
||||
// Prepare field data in an associative array.
|
||||
$edit = [];
|
||||
foreach ($modules as $module) {
|
||||
$edit[sprintf('reinstall[%s]', $module)] = TRUE;
|
||||
}
|
||||
|
||||
$this->drupalGet('devel/reinstall');
|
||||
$this->submitForm($edit, 'Reinstall');
|
||||
$this->assertSession()->pageTextContains('Uninstalled and installed: ' . implode(', ', $modules) . '.');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\path_alias\Entity\PathAlias;
|
||||
|
||||
/**
|
||||
* Tests the path alias devel page.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelPathAliasTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['devel', 'node', 'path_alias'];
|
||||
|
||||
public function testPathAliasDevelPage() {
|
||||
$this->drupalGet('devel/path-alias/node/999');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
$node = $this->drupalCreateNode();
|
||||
$node_id = $node->id();
|
||||
|
||||
$this->drupalGet('devel/path-alias/node/' . $node_id);
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
$this->drupalLogin($this->develUser);
|
||||
|
||||
$this->drupalGet('devel/path-alias/node/' . $node_id);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Found no aliases with path "/node/' . $node_id . '".');
|
||||
|
||||
PathAlias::create([
|
||||
'path' => '/node/' . $node_id,
|
||||
'alias' => '/custom-path-1',
|
||||
])->save();
|
||||
PathAlias::create([
|
||||
'path' => '/node/' . $node_id,
|
||||
'alias' => '/custom-path-2',
|
||||
])->save();
|
||||
|
||||
$this->drupalGet('devel/path-alias/node/' . $node_id);
|
||||
$this->assertSession()->pageTextContains('Found 2 aliases with path "/node/' . $node_id . '".');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
/**
|
||||
* Tests devel requirements.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelRequirementsTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* Tests that the status page shows a warning when evel is enabled.
|
||||
*/
|
||||
public function testStatusPage(): void {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$this->assertSession()->pageTextContains('Devel module enabled');
|
||||
$this->assertSession()->pageTextContains('The Devel module provides access to internal debugging information; therefore it\'s recommended to disable this module on sites in production.');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Tests routes info pages and links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelRouteInfoTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
$this->drupalLogin($this->develUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests routes info.
|
||||
*/
|
||||
public function testRouteList(): void {
|
||||
// Ensures that the routes info link is present on the devel menu and that
|
||||
// it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('Routes Info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/routes');
|
||||
$this->assertSession()->pageTextContains('Routes');
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
$headers = $page->findAll('css', 'table.devel-route-list thead th');
|
||||
$this->assertEquals(4, count($headers));
|
||||
|
||||
$expected_items = ['Route Name', 'Path', 'Allowed Methods', 'Operations'];
|
||||
foreach ($headers as $key => $element) {
|
||||
$this->assertSame($element->getText(), $expected_items[$key]);
|
||||
}
|
||||
|
||||
// Ensures that all the routes are listed in the table.
|
||||
$routes = \Drupal::service('router.route_provider')->getAllRoutes();
|
||||
$rows = $page->findAll('css', 'table.devel-route-list tbody tr');
|
||||
$this->assertEquals(count($routes), count($rows));
|
||||
|
||||
// Tests the presence of some (arbitrarily chosen) routes in the table.
|
||||
$expected_routes = [
|
||||
'<current>' => [
|
||||
'path' => '/<current>',
|
||||
'methods' => ['GET', 'POST'],
|
||||
'dynamic' => FALSE,
|
||||
],
|
||||
'user.login' => [
|
||||
'path' => '/user/login',
|
||||
'methods' => ['GET', 'POST'],
|
||||
'dynamic' => FALSE,
|
||||
],
|
||||
'entity.user.canonical' => [
|
||||
'path' => '/user/{user}',
|
||||
'methods' => ['GET', 'POST'],
|
||||
'dynamic' => TRUE,
|
||||
],
|
||||
'entity.user.devel_load' => [
|
||||
'path' => '/devel/user/{user}',
|
||||
'methods' => ['ANY'],
|
||||
'dynamic' => TRUE,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($expected_routes as $route_name => $expected) {
|
||||
$row = $page->find('css', sprintf('table.devel-route-list tbody tr:contains("%s")', $route_name));
|
||||
$this->assertNotNull($row);
|
||||
|
||||
$cells = $row->findAll('css', 'td');
|
||||
$this->assertEquals(4, count($cells));
|
||||
|
||||
$cell_route_name = $cells[0];
|
||||
$this->assertEquals($route_name, $cell_route_name->getText());
|
||||
$this->assertTrue($cell_route_name->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell_path = $cells[1];
|
||||
$this->assertEquals($expected['path'], $cell_path->getText());
|
||||
$this->assertTrue($cell_path->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell_methods = $cells[2];
|
||||
$this->assertEquals(implode('', $expected['methods']), $cell_methods->getText());
|
||||
|
||||
$cell_operations = $cells[3];
|
||||
$actual_href = $cell_operations->findLink('Devel')->getAttribute('href');
|
||||
if ($expected['dynamic']) {
|
||||
$options = ['query' => ['route_name' => $route_name]];
|
||||
}
|
||||
else {
|
||||
$options = ['query' => ['path' => $expected['path']]];
|
||||
}
|
||||
|
||||
$expected_href = Url::fromRoute('devel.route_info.item', [], $options)->toString();
|
||||
$this->assertEquals($expected_href, $actual_href);
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible only to the users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/routes');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests route detail page.
|
||||
*/
|
||||
public function testRouteDetail(): void {
|
||||
$expected_title = 'Route detail';
|
||||
$xpath_warning_messages = '//div[@aria-label="Warning message"]';
|
||||
|
||||
// Ensures that devel route detail link in the menu works properly.
|
||||
$url = $this->develUser->toUrl();
|
||||
$path = '/' . $url->getInternalPath();
|
||||
|
||||
$this->drupalGet($url);
|
||||
$this->clickLink('Current route info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($expected_title);
|
||||
// Absolute needed due to https://www.drupal.org/project/drupal/issues/3398551#comment-15313236.
|
||||
$expected_url = Url::fromRoute('devel.route_info.item', [], ['absolute' => TRUE, 'query' => ['path' => $path]]);
|
||||
$this->assertSession()->addressEquals($expected_url->toString());
|
||||
$this->assertSession()->elementNotExists('xpath', $xpath_warning_messages);
|
||||
|
||||
// Ensures that devel route detail works properly even when dynamic cache
|
||||
// is enabled.
|
||||
$url = Url::fromRoute('devel.simple_page');
|
||||
$path = '/' . $url->getInternalPath();
|
||||
|
||||
$this->drupalGet($url);
|
||||
$this->clickLink('Current route info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($expected_title);
|
||||
$expected_url = Url::fromRoute('devel.route_info.item', [], ['absolute' => TRUE, 'query' => ['path' => $path]]);
|
||||
$this->assertSession()->addressEquals($expected_url->toString());
|
||||
$this->assertSession()->elementNotExists('xpath', $xpath_warning_messages);
|
||||
|
||||
// Ensures that if a non existent path is passed as input, a warning
|
||||
// message is shown.
|
||||
$this->drupalGet('devel/routes/item', ['query' => ['path' => '/undefined']]);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($expected_title);
|
||||
$this->assertSession()->elementExists('xpath', $xpath_warning_messages);
|
||||
|
||||
// Ensures that the route detail page works properly when a valid route
|
||||
// name input is passed.
|
||||
$this->drupalGet('devel/routes/item', ['query' => ['route_name' => 'devel.simple_page']]);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($expected_title);
|
||||
$this->assertSession()->elementNotExists('xpath', $xpath_warning_messages);
|
||||
|
||||
// Ensures that if a non existent route name is passed as input a warning
|
||||
// message is shown.
|
||||
$this->drupalGet('devel/routes/item', ['query' => ['route_name' => 'not.exists']]);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($expected_title);
|
||||
$this->assertSession()->elementExists('xpath', $xpath_warning_messages);
|
||||
|
||||
// Ensures that if no 'path' nor 'name' query string is passed as input,
|
||||
// devel route detail page does not return errors.
|
||||
$this->drupalGet('devel/routes/item');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($expected_title);
|
||||
|
||||
// Ensures that the page is accessible ony to the users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/routes/item');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
/**
|
||||
* Tests routes rebuild.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelRouterRebuildTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* Test routes rebuild.
|
||||
*/
|
||||
public function testRouterRebuildConfirmForm(): void {
|
||||
// Reset the state flag.
|
||||
\Drupal::state()->set('devel_test_route_rebuild', NULL);
|
||||
|
||||
$this->drupalGet('devel/menu/reset');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->drupalGet('devel/menu/reset');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Are you sure you want to rebuild the router?');
|
||||
$route_rebuild_state = \Drupal::state()->get('devel_test_route_rebuild');
|
||||
$this->assertEmpty($route_rebuild_state);
|
||||
|
||||
$this->submitForm([], 'Rebuild');
|
||||
$this->assertSession()->pageTextContains('The router has been rebuilt.');
|
||||
$route_rebuild_state = \Drupal::state()->get('devel_test_route_rebuild');
|
||||
$this->assertEquals('Router rebuild fired', $route_rebuild_state);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Behat\Mink\Element\NodeElement;
|
||||
|
||||
/**
|
||||
* Tests devel state editor.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelStateEditorTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* The state store.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->state = $this->container->get('state');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests state editor menu link.
|
||||
*/
|
||||
public function testStateEditMenuLink(): void {
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
$this->drupalLogin($this->develUser);
|
||||
// Ensures that the state editor link is present on the devel menu and that
|
||||
// it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('State editor');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/state');
|
||||
$this->assertSession()->pageTextContains('State editor');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests state listing.
|
||||
*/
|
||||
public function testStateListing(): void {
|
||||
$table_selector = 'table.devel-state-list';
|
||||
|
||||
// Ensure that state listing page is accessible only by users with the
|
||||
// adequate permissions.
|
||||
$this->drupalGet('devel/state');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
$this->drupalLogin($this->develUser);
|
||||
$this->drupalGet('devel/state');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('State editor');
|
||||
|
||||
// Ensure that the state variables table is visible.
|
||||
$table = $this->assertSession()->elementExists('css', $table_selector);
|
||||
|
||||
// Ensure that all state variables are listed in the table.
|
||||
$states = \Drupal::keyValue('state')->getAll();
|
||||
$rows = $table->findAll('css', 'tbody tr');
|
||||
$this->assertEquals(count($rows), count($states), 'All states are listed in the table.');
|
||||
|
||||
// Ensure that the added state variables are listed in the table.
|
||||
$this->state->set('devel.simple', 'Hello!');
|
||||
$this->drupalGet('devel/state');
|
||||
$table = $this->assertSession()->elementExists('css', $table_selector);
|
||||
$this->assertSession()->elementExists('css', sprintf('tbody td:contains("%s")', 'devel.simple'), $table);
|
||||
|
||||
// Ensure that the operations column and the actions buttons are not
|
||||
// available for user without 'administer site configuration' permission.
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(count($headers), 2, 'Correct number of table header cells found.');
|
||||
$this->assertElementsTextEquals($headers, ['Name', 'Value']);
|
||||
$this->assertSession()->elementNotExists('css', 'ul.dropbutton li a', $table);
|
||||
|
||||
// Ensure that the operations column and the actions buttons are
|
||||
// available for user with 'administer site configuration' permission.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('devel/state');
|
||||
|
||||
$table = $this->assertSession()->elementExists('css', $table_selector);
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(count($headers), 3, 'Correct number of table header cells found.');
|
||||
$this->assertElementsTextEquals($headers, ['Name', 'Value', 'Operations']);
|
||||
$this->assertSession()->elementExists('css', 'ul.dropbutton li a', $table);
|
||||
|
||||
// Test that the edit button works properly.
|
||||
$this->clickLink('Edit');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests state edit.
|
||||
*/
|
||||
public function testStateEdit(): void {
|
||||
// Create some state variables for the test.
|
||||
$this->state->set('devel.simple', 0);
|
||||
$this->state->set('devel.array', ['devel' => 'value']);
|
||||
$this->state->set('devel.object', $this->randomObject());
|
||||
|
||||
// Ensure that state edit form is accessible only by users with the
|
||||
// adequate permissions.
|
||||
$this->drupalLogin($this->develUser);
|
||||
$this->drupalGet('devel/state/edit/devel.simple');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Ensure that accessing an un-existent state variable cause a warning
|
||||
// message.
|
||||
$this->drupalGet('devel/state/edit/devel.unknown');
|
||||
$this->assertSession()->pageTextContains(strtr('State @name does not exist in the system.', ['@name' => 'devel.unknown']));
|
||||
|
||||
// Ensure that state variables that contain simple type can be edited and
|
||||
// saved.
|
||||
$this->drupalGet('devel/state/edit/devel.simple');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains(strtr('Edit state variable: @name', ['@name' => 'devel.simple']));
|
||||
$input = $this->assertSession()->fieldExists('edit-new-value');
|
||||
$this->assertFalse($input->hasAttribute('disabled'));
|
||||
$button = $this->assertSession()->buttonExists('edit-submit');
|
||||
$this->assertFalse($button->hasAttribute('disabled'));
|
||||
|
||||
$edit = ['new_value' => 1];
|
||||
$this->submitForm($edit, 'Save');
|
||||
$this->assertSession()->pageTextContains(strtr('Variable @name was successfully edited.', ['@name' => 'devel.simple']));
|
||||
$this->assertEquals(1, $this->state->get('devel.simple'));
|
||||
|
||||
// Ensure that state variables that contain array can be edited and saved
|
||||
// and the new value is properly validated.
|
||||
$this->drupalGet('devel/state/edit/devel.array');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains(strtr('Edit state variable: @name', ['@name' => 'devel.array']));
|
||||
$input = $this->assertSession()->fieldExists('edit-new-value');
|
||||
$this->assertFalse($input->hasAttribute('disabled'));
|
||||
$button = $this->assertSession()->buttonExists('edit-submit');
|
||||
$this->assertFalse($button->hasAttribute('disabled'));
|
||||
|
||||
// Try to save an invalid yaml input.
|
||||
$edit = ['new_value' => 'devel: \'value updated'];
|
||||
$this->submitForm($edit, 'Save');
|
||||
$this->assertSession()->pageTextContains('Invalid input:');
|
||||
|
||||
$edit = ['new_value' => 'devel: \'value updated\''];
|
||||
$this->submitForm($edit, 'Save');
|
||||
$this->assertSession()->pageTextContains(strtr('Variable @name was successfully edited.', ['@name' => 'devel.array']));
|
||||
$this->assertEquals(['devel' => 'value updated'], $this->state->get('devel.array'));
|
||||
|
||||
// Ensure that state variables that contain objects cannot be edited.
|
||||
$this->drupalGet('devel/state/edit/devel.object');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains(strtr('Edit state variable: @name', ['@name' => 'devel.object']));
|
||||
$this->assertSession()->pageTextContains(strtr('Only simple structures are allowed to be edited. State @name contains objects.', ['@name' => 'devel.object']));
|
||||
$this->assertSession()->fieldDisabled('edit-new-value');
|
||||
$button = $this->assertSession()->buttonExists('edit-submit');
|
||||
$this->assertTrue($button->hasAttribute('disabled'));
|
||||
|
||||
// Ensure that the cancel link works as expected.
|
||||
$this->clickLink('Cancel');
|
||||
$this->assertSession()->addressEquals('devel/state');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the passed in elements have the expected text.
|
||||
*
|
||||
* @param \Behat\Mink\Element\NodeElement[] $elements
|
||||
* The elements for which check the text.
|
||||
* @param array $expected_elements_text
|
||||
* The expected text for the passed in elements.
|
||||
*/
|
||||
protected function assertElementsTextEquals(array $elements, array $expected_elements_text) {
|
||||
$actual_text = array_map(static fn(NodeElement $element) => $element->getText(), $elements);
|
||||
$this->assertSame($expected_elements_text, $actual_text);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Tests switch user.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelSwitchUserTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* The block used by this test.
|
||||
*
|
||||
* @var \Drupal\block\BlockInterface
|
||||
*/
|
||||
protected $block;
|
||||
|
||||
/**
|
||||
* The switch user.
|
||||
*
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $switchUser;
|
||||
|
||||
/**
|
||||
* The web user.
|
||||
*
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* The long user with maximum length username.
|
||||
*
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $longUser;
|
||||
|
||||
/**
|
||||
* Set up test.
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->block = $this->drupalPlaceBlock('devel_switch_user', ['id' => 'switch-user', 'label' => 'Switch Hit']);
|
||||
|
||||
$this->develUser = $this->drupalCreateUser(['access devel information', 'switch users'], 'Devel User Four');
|
||||
$this->switchUser = $this->drupalCreateUser(['switch users'], 'Switch User Five');
|
||||
$this->webUser = $this->drupalCreateUser([], 'Web User Six');
|
||||
$this->longUser = $this->drupalCreateUser([], 'Long User Seven name has the maximum length of 60 characters');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests switch user basic functionality.
|
||||
*/
|
||||
public function testSwitchUserFunctionality(): void {
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
$this->drupalGet('');
|
||||
$this->assertSession()->pageTextNotContains($this->block->label());
|
||||
|
||||
// Ensure that a token is required to switch user.
|
||||
$this->drupalGet('/devel/switch/' . $this->webUser->getDisplayName());
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
$this->drupalLogin($this->develUser);
|
||||
|
||||
$this->drupalGet('');
|
||||
$this->assertSession()->pageTextContains($this->block->label());
|
||||
|
||||
// Ensure that if name in not passed the controller returns access denied.
|
||||
$this->drupalGet('/devel/switch');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Ensure that a token is required to switch user.
|
||||
$this->drupalGet('/devel/switch/' . $this->switchUser->getDisplayName());
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Switch to another user account.
|
||||
$this->drupalGet('/user/' . $this->switchUser->id());
|
||||
$this->clickLink($this->switchUser->getDisplayName());
|
||||
$this->assertSessionByUid($this->switchUser->id());
|
||||
$this->assertNoSessionByUid($this->develUser->id());
|
||||
|
||||
// Switch back to initial account.
|
||||
$this->clickLink($this->develUser->getDisplayName());
|
||||
$this->assertNoSessionByUid($this->switchUser->id());
|
||||
$this->assertSessionByUid($this->develUser->id());
|
||||
|
||||
// Use the search form to switch to another account.
|
||||
$edit = ['userid' => $this->switchUser->getDisplayName()];
|
||||
$this->submitForm($edit, 'Switch');
|
||||
$this->assertSessionByUid($this->switchUser->id());
|
||||
$this->assertNoSessionByUid($this->develUser->id());
|
||||
|
||||
// Use the form with username of the maximum length. Mimic the autofill
|
||||
// result by adding " (userid)" at the end.
|
||||
$edit = ['userid' => $this->longUser->getDisplayName() . sprintf(' (%s)', $this->longUser->id())];
|
||||
$this->submitForm($edit, 'Switch');
|
||||
$this->assertSessionByUid($this->longUser->id());
|
||||
$this->assertNoSessionByUid($this->switchUser->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the switch user block configuration.
|
||||
*/
|
||||
public function testSwitchUserBlockConfiguration(): void {
|
||||
$anonymous = \Drupal::config('user.settings')->get('anonymous');
|
||||
|
||||
// Create some users for the test.
|
||||
for ($i = 0; $i < 12; ++$i) {
|
||||
$this->drupalCreateUser();
|
||||
}
|
||||
|
||||
$this->drupalLogin($this->develUser);
|
||||
|
||||
$this->drupalGet('');
|
||||
$this->assertSession()->pageTextContains($this->block->label());
|
||||
|
||||
// Ensure that block default configuration is effectively used. The block
|
||||
// default configuration is the following:
|
||||
// - list_size : 12.
|
||||
// - include_anon : FALSE.
|
||||
// - show_form : TRUE.
|
||||
$this->assertSwitchUserSearchForm();
|
||||
$this->assertSwitchUserListCount(12);
|
||||
$this->assertSwitchUserListNoContainsUser($anonymous);
|
||||
|
||||
// Ensure that changing the list_size configuration property the number of
|
||||
// user displayed in the list change.
|
||||
$this->setBlockConfiguration('list_size', 4);
|
||||
$this->drupalGet('');
|
||||
$this->assertSwitchUserListCount(4);
|
||||
|
||||
// Ensure that changing the include_anon configuration property the
|
||||
// anonymous user is displayed in the list.
|
||||
$this->setBlockConfiguration('include_anon', TRUE);
|
||||
$this->drupalGet('');
|
||||
$this->assertSwitchUserListContainsUser($anonymous);
|
||||
|
||||
// Ensure that changing the show_form configuration property the
|
||||
// form is not displayed.
|
||||
$this->setBlockConfiguration('show_form', FALSE);
|
||||
$this->drupalGet('');
|
||||
$this->assertSwitchUserNoSearchForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the user list items.
|
||||
*/
|
||||
public function testSwitchUserListItems(): void {
|
||||
$anonymous = \Drupal::config('user.settings')->get('anonymous');
|
||||
|
||||
$this->setBlockConfiguration('list_size', 2);
|
||||
|
||||
// Login as web user so we are sure that this account is prioritized
|
||||
// in the list if not enough users with 'switch users' permission are
|
||||
// present.
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
$this->drupalLogin($this->develUser);
|
||||
$this->drupalGet('');
|
||||
|
||||
// Ensure that users with 'switch users' permission are prioritized.
|
||||
$this->assertSwitchUserListCount(2);
|
||||
$this->assertSwitchUserListContainsUser($this->develUser->getDisplayName());
|
||||
$this->assertSwitchUserListContainsUser($this->switchUser->getDisplayName());
|
||||
|
||||
// Ensure that blocked users are not shown in the list.
|
||||
$this->switchUser->set('status', 0)->save();
|
||||
$this->drupalGet('');
|
||||
$this->assertSwitchUserListCount(2);
|
||||
$this->assertSwitchUserListContainsUser($this->develUser->getDisplayName());
|
||||
$this->assertSwitchUserListContainsUser($this->webUser->getDisplayName());
|
||||
$this->assertSwitchUserListNoContainsUser($this->switchUser->getDisplayName());
|
||||
|
||||
// Ensure that anonymous user are prioritized if include_anon is set to
|
||||
// true.
|
||||
$this->setBlockConfiguration('include_anon', TRUE);
|
||||
$this->drupalGet('');
|
||||
$this->assertSwitchUserListCount(2);
|
||||
$this->assertSwitchUserListContainsUser($this->develUser->getDisplayName());
|
||||
$this->assertSwitchUserListContainsUser($anonymous);
|
||||
|
||||
// Ensure that the switch user block works properly even if no prioritized
|
||||
// users are found (special handling for user 1).
|
||||
$this->drupalLogout();
|
||||
$this->develUser->delete();
|
||||
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$this->drupalGet('');
|
||||
$this->assertSwitchUserListCount(2);
|
||||
// Removed assertion on rootUser which causes random test failures.
|
||||
// @todo Adjust the tests when user 1 option is completed.
|
||||
// @see https://www.drupal.org/project/devel/issues/3097047
|
||||
// @see https://www.drupal.org/project/devel/issues/3114264
|
||||
$this->assertSwitchUserListContainsUser($anonymous);
|
||||
|
||||
$roleStorage = \Drupal::entityTypeManager()->getStorage('user_role');
|
||||
|
||||
// Ensure that the switch user block works properly even if no roles have
|
||||
// the 'switch users' permission associated (special handling for user 1).
|
||||
/** @var array<string, \Drupal\user\RoleInterface> $roles */
|
||||
$roles = $roleStorage->loadMultiple();
|
||||
unset($roles[AccountInterface::ANONYMOUS_ROLE]);
|
||||
$roles = array_filter($roles, static fn($role): bool => $role->hasPermission('switch users'));
|
||||
$roleStorage->delete($roles);
|
||||
|
||||
$this->drupalGet('');
|
||||
$this->assertSwitchUserListCount(2);
|
||||
// Removed assertion on rootUser which causes random test failures.
|
||||
// @todo Adjust the tests when user 1 option is completed.
|
||||
// @see https://www.drupal.org/project/devel/issues/3097047
|
||||
// @see https://www.drupal.org/project/devel/issues/3114264
|
||||
$this->assertSwitchUserListContainsUser($anonymous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for verify the number of items shown in the user list.
|
||||
*
|
||||
* @param int $number
|
||||
* The expected number of items.
|
||||
*/
|
||||
public function assertSwitchUserListCount($number): void {
|
||||
$result = $this->xpath('//div[@id=:block]//ul/li/a', [':block' => 'block-switch-user']);
|
||||
$this->assertCount($number, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for verify if the user list contains a username.
|
||||
*
|
||||
* @param string $username
|
||||
* The username to check.
|
||||
*/
|
||||
public function assertSwitchUserListContainsUser($username): void {
|
||||
$result = $this->xpath('//div[@id=:block]//ul/li/a[normalize-space()=:user]', [':block' => 'block-switch-user', ':user' => $username]);
|
||||
$this->assertTrue(count($result) > 0, new FormattableMarkup('User "%user" is included in the switch user list.', ['%user' => $username]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for verify if the user list not contains a username.
|
||||
*
|
||||
* @param string $username
|
||||
* The username to check.
|
||||
*/
|
||||
public function assertSwitchUserListNoContainsUser($username): void {
|
||||
$result = $this->xpath('//div[@id=:block]//ul/li/a[normalize-space()=:user]', [':block' => 'block-switch-user', ':user' => $username]);
|
||||
$this->assertTrue(count($result) == 0, new FormattableMarkup('User "%user" is not included in the switch user list.', ['%user' => $username]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for verify if the search form is shown.
|
||||
*/
|
||||
public function assertSwitchUserSearchForm(): void {
|
||||
$result = $this->xpath('//div[@id=:block]//form[contains(@class, :form)]', [':block' => 'block-switch-user', ':form' => 'devel-switchuser-form']);
|
||||
$this->assertTrue(count($result) > 0, 'The search form is shown.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for verify if the search form is not shown.
|
||||
*/
|
||||
public function assertSwitchUserNoSearchForm(): void {
|
||||
$result = $this->xpath('//div[@id=:block]//form[contains(@class, :form)]', [':block' => 'block-switch-user', ':form' => 'devel-switchuser-form']);
|
||||
$this->assertTrue(count($result) == 0, 'The search form is not shown.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected helper method to set the test block's configuration.
|
||||
*/
|
||||
protected function setBlockConfiguration($key, $value) {
|
||||
$block = $this->block->getPlugin();
|
||||
$block->setConfigurationValue($key, $value);
|
||||
$this->block->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that there is a session for a given user ID.
|
||||
*
|
||||
* Based off masquarade module.
|
||||
*
|
||||
* @param int $uid
|
||||
* The user ID for which to find a session record.
|
||||
*
|
||||
* @todo find a cleaner way to do this check.
|
||||
*/
|
||||
protected function assertSessionByUid($uid) {
|
||||
$result = \Drupal::database()
|
||||
->select('sessions')
|
||||
->fields('sessions', ['uid'])
|
||||
->condition('uid', $uid)
|
||||
->execute()->fetchAll();
|
||||
|
||||
// Check that we have some results.
|
||||
$this->assertNotEmpty($result, sprintf('No session found for uid %s', $uid));
|
||||
// If there is more than one session, then that must be unexpected.
|
||||
$this->assertCount(1, $result, sprintf('Found more than one session for uid %s', $uid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that no session exists for a given uid.
|
||||
*
|
||||
* Based off masquarade module.
|
||||
*
|
||||
* @param int $uid
|
||||
* The user ID to assert.
|
||||
*
|
||||
* @todo find a cleaner way to do this check.
|
||||
*/
|
||||
protected function assertNoSessionByUid($uid) {
|
||||
$result = \Drupal::database()
|
||||
->select('sessions')
|
||||
->fields('sessions', ['uid'])
|
||||
->condition('uid', $uid)
|
||||
->execute()->fetchAll();
|
||||
|
||||
$this->assertEmpty($result, sprintf('No session for uid %d found.', $uid));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,270 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
|
||||
/**
|
||||
* Tests devel toolbar module functionality.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelToolbarTest extends DevelBrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['devel', 'toolbar', 'block', 'node'];
|
||||
|
||||
/**
|
||||
* The user for tests.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $toolbarUser;
|
||||
|
||||
/**
|
||||
* The default toolbar items.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultToolbarItems = [
|
||||
'devel.cache_clear',
|
||||
'devel.container_info.service',
|
||||
'devel.admin_settings_link',
|
||||
'devel.menu_rebuild',
|
||||
'devel.reinstall',
|
||||
'devel.route_info',
|
||||
'devel.run_cron',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
|
||||
$this->develUser = $this->drupalCreateUser([
|
||||
'administer site configuration',
|
||||
'access devel information',
|
||||
'access toolbar',
|
||||
'switch users',
|
||||
]);
|
||||
$this->toolbarUser = $this->drupalCreateUser([
|
||||
'access toolbar',
|
||||
'switch users',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuration form.
|
||||
*/
|
||||
public function testConfigurationForm(): void {
|
||||
// Ensures that the page is accessible only to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalGet('admin/config/development/devel/toolbar');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Ensures that the config page is accessible for users with the adequate
|
||||
// permissions and the Devel toolbar local task and content are shown.
|
||||
$this->drupalLogin($this->develUser);
|
||||
$this->drupalGet('admin/config/development/devel/toolbar');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()
|
||||
->elementExists('xpath', '//h2[text()="Primary tabs"]/following-sibling::ul//a[contains(text(), "Toolbar Settings")]');
|
||||
$this->assertSession()->elementExists('xpath', '//fieldset[@id="edit-toolbar-items--wrapper"]');
|
||||
$this->assertSession()->pageTextContains('Devel Toolbar Settings');
|
||||
|
||||
// Ensures and that all devel menu links are listed in the configuration
|
||||
// page.
|
||||
foreach ($this->getMenuLinkInfos() as $link) {
|
||||
$this->assertSession()->fieldExists(sprintf('toolbar_items[%s]', $link['id']));
|
||||
}
|
||||
|
||||
// Ensures and that the default configuration items are selected by
|
||||
// default.
|
||||
foreach ($this->defaultToolbarItems as $item) {
|
||||
$this->assertSession()->checkboxChecked(sprintf('toolbar_items[%s]', $item));
|
||||
}
|
||||
|
||||
// Ensures that the configuration save works as expected.
|
||||
$edit = [
|
||||
'toolbar_items[devel.event_info]' => 'devel.event_info',
|
||||
'toolbar_items[devel.theme_registry]' => 'devel.theme_registry',
|
||||
];
|
||||
$this->submitForm($edit, 'Save configuration');
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
|
||||
$expected_items = array_merge($this->defaultToolbarItems, ['devel.event_info', 'devel.theme_registry']);
|
||||
sort($expected_items);
|
||||
$config_items = \Drupal::config('devel.toolbar.settings')->get('toolbar_items');
|
||||
sort($config_items);
|
||||
|
||||
$this->assertEquals($expected_items, $config_items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests cache metadata headers.
|
||||
*/
|
||||
public function testCacheHeaders(): void {
|
||||
// Disable user toolbar tab so we can test properly if the devel toolbar
|
||||
// implementation interferes with the page cacheability.
|
||||
\Drupal::service('module_installer')->install(['toolbar_disable_user_toolbar']);
|
||||
|
||||
// The menu is not loaded for users without the adequate permission,
|
||||
// so no cache tags for configuration are added.
|
||||
$this->drupalLogin($this->toolbarUser);
|
||||
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'config:devel.toolbar.settings');
|
||||
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'config:system.menu.devel');
|
||||
|
||||
// Make sure that the configuration cache tags are present for users with
|
||||
// the adequate permission.
|
||||
$this->drupalLogin($this->develUser);
|
||||
|
||||
// Dont go to the user/n page as thats uncacheable per https://gitlab.com/drupalspoons/devel/-/issues/541#note_2113393769
|
||||
$node_type = $this->drupalCreateContentType();
|
||||
$node = $this->drupalCreateNode(['type' => $node_type->id()]);
|
||||
$this->drupalGet($node->toUrl());
|
||||
|
||||
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:devel.toolbar.settings');
|
||||
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:system.menu.devel');
|
||||
|
||||
// The Devel toolbar implementation should not interfere with the page
|
||||
// cacheability, so you expect a MISS value in the X-Drupal-Dynamic-Cache
|
||||
// header the first time.
|
||||
$this->assertSession()->responseHeaderContains('X-Drupal-Dynamic-Cache', 'MISS');
|
||||
|
||||
// Triggers a page reload and verify that the page is served from the
|
||||
// cache.
|
||||
$this->drupalGet($node->toUrl());
|
||||
$this->assertSession()->responseHeaderContains('X-Drupal-Dynamic-Cache', 'HIT');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests toolbar integration.
|
||||
*/
|
||||
public function testToolbarIntegration(): void {
|
||||
$library_css_url = 'css/devel.toolbar.css';
|
||||
$toolbar_selector = '#toolbar-bar .toolbar-tab';
|
||||
$toolbar_tab_selector = '#toolbar-bar .toolbar-tab a.toolbar-icon-devel';
|
||||
$toolbar_tray_selector = '#toolbar-bar .toolbar-tab #toolbar-item-devel-tray';
|
||||
|
||||
// Ensures that devel toolbar item is accessible only for user with the
|
||||
// adequate permissions.
|
||||
$this->drupalGet('');
|
||||
$this->assertSession()->responseNotContains($library_css_url);
|
||||
$this->assertSession()->elementNotExists('css', $toolbar_selector);
|
||||
$this->assertSession()->elementNotExists('css', $toolbar_tab_selector);
|
||||
|
||||
$this->drupalLogin($this->toolbarUser);
|
||||
$this->assertSession()->responseNotContains($library_css_url);
|
||||
$this->assertSession()->elementExists('css', $toolbar_selector);
|
||||
$this->assertSession()->elementNotExists('css', $toolbar_tab_selector);
|
||||
|
||||
$this->drupalLogin($this->develUser);
|
||||
$this->assertSession()->responseContains($library_css_url);
|
||||
$this->assertSession()->elementExists('css', $toolbar_selector);
|
||||
$this->assertSession()->elementExists('css', $toolbar_tab_selector);
|
||||
$this->assertSession()->elementTextContains('css', $toolbar_tab_selector, 'Devel');
|
||||
|
||||
// Ensures that the configure link in the toolbar is present and point to
|
||||
// the correct page.
|
||||
$this->clickLink('Configure');
|
||||
$this->assertSession()->addressEquals('admin/config/development/devel/toolbar');
|
||||
|
||||
// Ensures that the toolbar tray contains the all the menu links. To the
|
||||
// links not marked as always visible will be assigned a css class that
|
||||
// allow to hide they when the toolbar has horizontal orientation.
|
||||
$this->drupalGet('');
|
||||
$toolbar_tray = $this->assertSession()->elementExists('css', $toolbar_tray_selector);
|
||||
|
||||
$devel_menu_items = $this->getMenuLinkInfos();
|
||||
$toolbar_items = $toolbar_tray->findAll('css', 'ul.toolbar-menu a');
|
||||
$this->assertCount(count($devel_menu_items), $toolbar_items);
|
||||
|
||||
foreach ($devel_menu_items as $link) {
|
||||
$item_selector = sprintf('ul.toolbar-menu a:contains("%s")', $link['title']);
|
||||
$item = $this->assertSession()->elementExists('css', $item_selector, $toolbar_tray);
|
||||
// Only test the url up to the ? as the destination and token parameters
|
||||
// will vary and are not checkable.
|
||||
$this->assertEquals(strtok($link['url'], '?'), strtok($item->getAttribute('href'), '?'));
|
||||
|
||||
$not_visible = !in_array($link['id'], $this->defaultToolbarItems);
|
||||
$this->assertTrue($not_visible === $item->hasClass('toolbar-horizontal-item-hidden'));
|
||||
}
|
||||
|
||||
// Ensures that changing the toolbar settings configuration the changes are
|
||||
// immediately visible.
|
||||
$saved_items = $this->config('devel.toolbar.settings')->get('toolbar_items');
|
||||
$saved_items[] = 'devel.event_info';
|
||||
|
||||
$this->config('devel.toolbar.settings')
|
||||
->set('toolbar_items', $saved_items)
|
||||
->save();
|
||||
|
||||
$this->drupalGet('');
|
||||
$toolbar_tray = $this->assertSession()->elementExists('css', $toolbar_tray_selector);
|
||||
$item = $this->assertSession()->elementExists('css', sprintf('ul.toolbar-menu a:contains("%s")', 'Events Info'), $toolbar_tray);
|
||||
$this->assertFalse($item->hasClass('toolbar-horizontal-item-hidden'));
|
||||
|
||||
// Ensures that disabling a menu link it will not more shown in the toolbar
|
||||
// and that the changes are immediately visible.
|
||||
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
|
||||
$menu_link_manager->updateDefinition('devel.event_info', ['enabled' => FALSE]);
|
||||
|
||||
$this->drupalGet('');
|
||||
$toolbar_tray = $this->assertSession()->elementExists('css', $toolbar_tray_selector);
|
||||
$this->assertSession()->elementNotExists('css', sprintf('ul.toolbar-menu a:contains("%s")', 'Events Info'), $toolbar_tray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel when toolbar module is not installed.
|
||||
*/
|
||||
public function testToolbarModuleNotInstalled(): void {
|
||||
// Ensures that when toolbar module is not installed all works properly.
|
||||
\Drupal::service('module_installer')->uninstall(['toolbar']);
|
||||
|
||||
$this->drupalLogin($this->develUser);
|
||||
|
||||
// Toolbar settings page should respond with 404.
|
||||
$this->drupalGet('admin/config/development/devel/toolbar');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Primary local task should not contain toolbar tab.
|
||||
$this->drupalGet('admin/config/development/devel');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->elementNotExists('xpath', '//a[contains(text(), "Toolbar Settings")]');
|
||||
|
||||
// Toolbar setting config and devel menu cache tags sholud not present.
|
||||
$this->drupalGet('');
|
||||
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'config:devel.toolbar.settings');
|
||||
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'config:system.menu.devel');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for retrieve the menu link informations.
|
||||
*
|
||||
* @return array
|
||||
* An array containing the menu link informations.
|
||||
*/
|
||||
protected function getMenuLinkInfos() {
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->onlyEnabledLinks()->setTopLevelOnly();
|
||||
$tree = \Drupal::menuTree()->load('devel', $parameters);
|
||||
|
||||
$links = [];
|
||||
foreach ($tree as $element) {
|
||||
$links[] = [
|
||||
'id' => $element->link->getPluginId(),
|
||||
'title' => $element->link->getTitle(),
|
||||
'url' => $element->link->getUrlObject()->toString(),
|
||||
];
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Provides convenience methods for assertions in browser tests.
|
||||
*/
|
||||
trait DevelWebAssertHelper {
|
||||
|
||||
/**
|
||||
* Asserts local tasks in the page output.
|
||||
*
|
||||
* @param array $routes
|
||||
* A list of expected local tasks, prepared as an array of route names and
|
||||
* their associated route parameters, to assert on the page (in the given
|
||||
* order).
|
||||
* @param int $level
|
||||
* (optional) The local tasks level to assert; 0 for primary, 1 for
|
||||
* secondary. Defaults to 0.
|
||||
*/
|
||||
protected function assertLocalTasks(array $routes, $level = 0) {
|
||||
$tab_label = $level == 0 ? 'Primary tabs' : 'Secondary tabs';
|
||||
$elements = $this->xpath('//h2[text()="' . $tab_label . '"]/following-sibling::ul//a');
|
||||
$this->assertNotEmpty($elements, 'Local tasks not found.');
|
||||
foreach ($routes as $index => $route_info) {
|
||||
[$route_name, $route_parameters] = $route_info;
|
||||
$expected = Url::fromRoute($route_name, $route_parameters)->toString();
|
||||
$this->assertEquals($expected, $elements[$index]->getAttribute('href'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Kernel;
|
||||
|
||||
/**
|
||||
* Provides a class for checking dumper output.
|
||||
*/
|
||||
trait DevelDumperTestTrait {
|
||||
|
||||
/**
|
||||
* Assertion for ensure dump content.
|
||||
*
|
||||
* Asserts that the string passed in input is equals to the string
|
||||
* representation of a variable obtained exporting the data.
|
||||
*
|
||||
* Use \Drupal\devel\DevelDumperManager::export().
|
||||
*
|
||||
* @param string $dump
|
||||
* The string that contains the dump output to test.
|
||||
* @param mixed $data
|
||||
* The variable to dump.
|
||||
* @param string|null $name
|
||||
* (optional) The label to output before variable, defaults to NULL.
|
||||
* @param string $message
|
||||
* (optional) A message to display with the assertion.
|
||||
*/
|
||||
public function assertDumpExportEquals($dump, mixed $data, ?string $name = NULL, $message = ''): void {
|
||||
$output = $this->getDumperExportDump($data, $name);
|
||||
$this->assertEquals(rtrim($dump), $output, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a haystack contains the dump export output.
|
||||
*
|
||||
* Use \Drupal\devel\DevelDumperManager::export().
|
||||
*
|
||||
* @param string $haystack
|
||||
* The string that contains the dump output to test.
|
||||
* @param mixed $data
|
||||
* The variable to dump.
|
||||
* @param string|null $name
|
||||
* (optional) The label to output before variable, defaults to NULL.
|
||||
* @param string $message
|
||||
* (optional) A message to display with the assertion.
|
||||
*/
|
||||
public function assertContainsDumpExport($haystack, mixed $data, ?string $name = NULL, $message = ''): void {
|
||||
// As at 18.04.2020 assertContainsDumpExport() is not actually used in any
|
||||
// devel tests in any current code branch.
|
||||
$output = $this->getDumperExportDump($data, $name);
|
||||
$this->assertStringContainsString($output, (string) $haystack, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assertion for ensure dump content.
|
||||
*
|
||||
* Asserts that the string passed in input is equals to the string
|
||||
* representation of a variable obtained dumping the data.
|
||||
*
|
||||
* Use \Drupal\devel\DevelDumperManager::dump().
|
||||
*
|
||||
* @param string $dump
|
||||
* The string that contains the dump output to test.
|
||||
* @param mixed $data
|
||||
* The variable to dump.
|
||||
* @param string|null $name
|
||||
* (optional) The label to output before variable, defaults to NULL.
|
||||
* @param string $message
|
||||
* (optional) A message to display with the assertion.
|
||||
*/
|
||||
public function assertDumpEquals($dump, mixed $data, ?string $name = NULL, $message = ''): void {
|
||||
$output = $this->getDumperDump($data, $name);
|
||||
$this->assertEquals(rtrim($dump), $output, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a haystack contains the dump output.
|
||||
*
|
||||
* Use \Drupal\devel\DevelDumperManager::dump().
|
||||
*
|
||||
* @param string $haystack
|
||||
* The string that contains the dump output to test.
|
||||
* @param mixed $data
|
||||
* The variable to dump.
|
||||
* @param string|null $name
|
||||
* (optional) The label to output before variable, defaults to NULL.
|
||||
* @param string $message
|
||||
* (optional) A message to display with the assertion.
|
||||
*/
|
||||
public function assertContainsDump($haystack, mixed $data, ?string $name = NULL, $message = ''): void {
|
||||
$output = $this->getDumperDump($data, $name);
|
||||
$this->assertStringContainsString($output, (string) $haystack, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of a variable.
|
||||
*
|
||||
* @param mixed $input
|
||||
* The variable to dump.
|
||||
* @param string|null $name
|
||||
* (optional) The label to output before variable, defaults to NULL.
|
||||
*
|
||||
* @return string
|
||||
* String representation of a variable.
|
||||
*
|
||||
* @see \Drupal\devel\DevelDumperManager::export()
|
||||
*/
|
||||
private function getDumperExportDump(mixed $input, ?string $name = NULL): string {
|
||||
$output = \Drupal::service('devel.dumper')->export($input, $name);
|
||||
return rtrim($output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of a variable.
|
||||
*
|
||||
* @param mixed $input
|
||||
* The variable to dump.
|
||||
* @param string|null $name
|
||||
* (optional) The label to output before variable, defaults to NULL.
|
||||
*
|
||||
* @return string
|
||||
* String representation of a variable.
|
||||
*
|
||||
* @see \Drupal\devel\DevelDumperManager::dump()
|
||||
*/
|
||||
private function getDumperDump(mixed $input, ?string $name = NULL): string {
|
||||
ob_start();
|
||||
\Drupal::service('devel.dumper')->dump($input, $name);
|
||||
$output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return rtrim($output);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\system\Entity\Menu;
|
||||
|
||||
/**
|
||||
* Tests Devel enforced dependencies.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelEnforcedDependenciesTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $modules = ['devel', 'block', 'user', 'system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig('devel');
|
||||
// For uninstall to work.
|
||||
$this->installSchema('user', ['users_data']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel menu enforced dependencies.
|
||||
*/
|
||||
public function testMenuEnforcedDependencies(): void {
|
||||
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
|
||||
$config_manager = $this->container->get('config.manager');
|
||||
|
||||
// Ensure that the Devel menu has explicit enforced dependencies on devel
|
||||
// module.
|
||||
$menu = Menu::load('devel');
|
||||
$this->assertEquals(['enforced' => ['module' => ['devel']]], $menu->get('dependencies'));
|
||||
|
||||
// Creates an instance of devel menu block so you can test if enforced
|
||||
// dependencies work properly with it.
|
||||
$block_id = strtolower($this->randomMachineName(8));
|
||||
|
||||
$block = Block::create([
|
||||
'plugin' => 'system_menu_block:devel',
|
||||
'region' => 'sidebar_first',
|
||||
'id' => $block_id,
|
||||
'theme' => $this->config('system.theme')->get('default'),
|
||||
'label' => $this->randomMachineName(8),
|
||||
'visibility' => [],
|
||||
'weight' => 0,
|
||||
]);
|
||||
$block->save();
|
||||
|
||||
// Ensure that the menu and block instance depend on devel module.
|
||||
$dependents = $config_manager->findConfigEntityDependencies('module', ['devel']);
|
||||
$this->assertArrayHasKey('system.menu.devel', $dependents);
|
||||
$this->assertArrayHasKey('block.block.' . $block_id, $dependents);
|
||||
|
||||
$this->container->get('module_installer')->uninstall(['devel']);
|
||||
|
||||
// Ensure that the menu and block instance are deleted when the dependency
|
||||
// is uninstalled.
|
||||
$this->assertNull(Menu::load('devel'));
|
||||
$this->assertNull(Block::load($block_id));
|
||||
|
||||
// Ensure that no config entities depend on devel once uninstalled.
|
||||
$dependents = $config_manager->findConfigEntityDependencies('module', ['devel']);
|
||||
$this->assertArrayNotHasKey('system.menu.devel', $dependents);
|
||||
$this->assertArrayNotHasKey('block.block.' . $block_id, $dependents);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Kernel;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;
|
||||
|
||||
/**
|
||||
* Test Load with References.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelEntityToArrayTest extends EntityKernelTestBase {
|
||||
|
||||
use EntityReferenceFieldCreationTrait;
|
||||
|
||||
/**
|
||||
* The entity type used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType = 'entity_test';
|
||||
|
||||
/**
|
||||
* The entity type that is being referenced.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $referencedEntityType = 'entity_test_rev';
|
||||
|
||||
/**
|
||||
* The bundle used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle = 'entity_test';
|
||||
|
||||
/**
|
||||
* The name of the field used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName = 'field_test';
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $modules = ['entity_test', 'devel'];
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
|
||||
$user = $this->createUser(permissions: ['access devel information'], values: ['name' => 'test']);
|
||||
|
||||
/** @var \Drupal\Core\Session\AccountProxyInterface $current_user */
|
||||
$current_user = $this->container->get('current_user');
|
||||
$current_user->setAccount($user);
|
||||
|
||||
// Create a field.
|
||||
$this->createEntityReferenceField(
|
||||
$this->entityType,
|
||||
$this->bundle,
|
||||
$this->fieldName,
|
||||
'Field test',
|
||||
$this->referencedEntityType,
|
||||
'default',
|
||||
['target_bundles' => [$this->bundle]],
|
||||
FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method.
|
||||
*/
|
||||
public function testWithReferences(): void {
|
||||
// Create the parent entity.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityType)
|
||||
->create(['type' => $this->bundle]);
|
||||
|
||||
// Create three target entities and attach them to parent field.
|
||||
$target_entities = [];
|
||||
$reference_field = [];
|
||||
for ($i = 0; $i < 3; ++$i) {
|
||||
$target_entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->referencedEntityType)
|
||||
->create([
|
||||
'type' => $this->bundle,
|
||||
'name' => 'Related ' . $i,
|
||||
]);
|
||||
$target_entity->save();
|
||||
$target_entities[] = $target_entity;
|
||||
$reference_field[]['target_id'] = $target_entity->id();
|
||||
}
|
||||
|
||||
// Set the field value.
|
||||
$entity->{$this->fieldName}->setValue($reference_field);
|
||||
$entity->save();
|
||||
|
||||
/** @var \Drupal\devel\DevelDumperManagerInterface $dumper */
|
||||
$dumper = $this->container->get('devel.dumper');
|
||||
$result = $dumper->export($entity, NULL, 'drupal_variable', TRUE);
|
||||
for ($i = 0; $i < 3; ++$i) {
|
||||
$this->assertStringContainsString('Related ' . $i, (string) $result, 'The referenced entities are present in the dumper output.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
204
web/modules/contrib/devel/tests/src/Kernel/DevelMailLogTest.php
Normal file
204
web/modules/contrib/devel/tests/src/Kernel/DevelMailLogTest.php
Normal file
@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Kernel;
|
||||
|
||||
use Drupal\Core\Mail\Plugin\Mail\TestMailCollector;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\devel\Plugin\Mail\DevelMailLog;
|
||||
|
||||
/**
|
||||
* Tests sending mails with debug interface.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelMailLogTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $modules = ['devel', 'devel_test', 'system'];
|
||||
|
||||
/**
|
||||
* The mail manager.
|
||||
*
|
||||
* @var \Drupal\Core\Mail\MailManagerInterface
|
||||
*/
|
||||
protected $mailManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->installConfig(['system', 'devel']);
|
||||
|
||||
// Configure system.site mail settings.
|
||||
$this->config('system.site')->set('mail', 'devel-test@example.com')->save();
|
||||
|
||||
$this->mailManager = $this->container->get('plugin.manager.mail');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel_mail_log plugin as default mail backend.
|
||||
*/
|
||||
public function testDevelMailLogDefaultBackend(): void {
|
||||
// Configure devel_mail_log as default mail backends.
|
||||
$this->setDevelMailLogAsDefaultBackend();
|
||||
|
||||
// Ensures that devel_mail_log is the default mail plugin .
|
||||
$mail_backend = $this->mailManager->getInstance(['module' => 'default', 'key' => 'default']);
|
||||
$this->assertInstanceOf(DevelMailLog::class, $mail_backend);
|
||||
|
||||
$mail_backend = $this->mailManager->getInstance(['module' => 'somemodule', 'key' => 'default']);
|
||||
$this->assertInstanceOf(DevelMailLog::class, $mail_backend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel_mail_log plugin with multiple mail backend.
|
||||
*/
|
||||
public function testDevelMailLogMultipleBackend(): void {
|
||||
// Configure test_mail_collector as default mail backend.
|
||||
$this->config('system.mail')
|
||||
->set('interface.default', 'test_mail_collector')
|
||||
->save();
|
||||
|
||||
// Configure devel_mail_log as a module-specific mail backend.
|
||||
$this->config('system.mail')
|
||||
->set('interface.somemodule', 'devel_mail_log')
|
||||
->save();
|
||||
|
||||
// Ensures that devel_mail_log is not the default mail plugin.
|
||||
$mail_backend = $this->mailManager->getInstance(['module' => 'default', 'key' => 'default']);
|
||||
$this->assertInstanceOf(TestMailCollector::class, $mail_backend);
|
||||
|
||||
// Ensures that devel_mail_log is used as mail backend only for the
|
||||
// specified module.
|
||||
$mail_backend = $this->mailManager->getInstance(['module' => 'somemodule', 'key' => 'default']);
|
||||
$this->assertInstanceOf(DevelMailLog::class, $mail_backend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel_mail_log default settings.
|
||||
*/
|
||||
public function testDevelMailDefaultSettings(): void {
|
||||
$config = \Drupal::config('devel.settings');
|
||||
$this->assertEquals('temporary://devel-mails', $config->get('debug_mail_directory'));
|
||||
$this->assertEquals('%to-%subject-%datetime.mail.txt', $config->get('debug_mail_file_format'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel mail log output.
|
||||
*/
|
||||
public function testDevelMailLogOutput(): void {
|
||||
$config = \Drupal::config('devel.settings');
|
||||
|
||||
/** @var \Drupal\Core\Language\LanguageManagerInterface $language_manager */
|
||||
$language_manager = $this->container->get('language_manager');
|
||||
|
||||
// Parameters used for send the email.
|
||||
$mail = [
|
||||
'module' => 'devel_test',
|
||||
'key' => 'devel_mail_log',
|
||||
'to' => 'drupal@example.com',
|
||||
'reply' => 'replyto@example.com',
|
||||
'lang' => $language_manager->getCurrentLanguage()->getId(),
|
||||
];
|
||||
|
||||
// Parameters used for compose the email in devel_test module.
|
||||
// @see devel_test_mail()
|
||||
$params = [
|
||||
'subject' => 'Devel mail log subject',
|
||||
'body' => 'Devel mail log body',
|
||||
'headers' => [
|
||||
'from' => 'postmaster@example.com',
|
||||
'additional' => [
|
||||
'X-stupid' => 'dumb',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Configure devel_mail_log as default mail backends.
|
||||
$this->setDevelMailLogAsDefaultBackend();
|
||||
|
||||
// Changes the default filename pattern removing the dynamic date
|
||||
// placeholder for a more predictable filename output.
|
||||
$random = $this->randomMachineName();
|
||||
$filename_pattern = '%to-%subject-' . $random . '.mail.txt';
|
||||
$this->config('devel.settings')
|
||||
->set('debug_mail_file_format', $filename_pattern)
|
||||
->save();
|
||||
|
||||
$expected_filename = 'drupal@example.com-Devel_mail_log_subject-' . $random . '.mail.txt';
|
||||
$expected_output = <<<EOF
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8; format=flowed; delsp=yes
|
||||
Content-Transfer-Encoding: 8Bit
|
||||
X-Mailer: Drupal
|
||||
Return-Path: devel-test@example.com
|
||||
Sender: devel-test@example.com
|
||||
From: postmaster@example.com
|
||||
Reply-to: replyto@example.com
|
||||
X-stupid: dumb
|
||||
To: drupal@example.com
|
||||
Subject: Devel mail log subject
|
||||
|
||||
Devel mail log body
|
||||
|
||||
EOF;
|
||||
|
||||
// Ensures that the mail is captured by devel_mail_log and the placeholders
|
||||
// in the filename are properly resolved.
|
||||
$default_output_directory = $config->get('debug_mail_directory');
|
||||
$expected_file_path = $default_output_directory . '/' . $expected_filename;
|
||||
|
||||
$this->mailManager->mail($mail['module'], $mail['key'], $mail['to'], $mail['lang'], $params, $mail['reply']);
|
||||
$this->assertFileExists($expected_file_path);
|
||||
$this->assertStringEqualsFile($expected_file_path, $expected_output);
|
||||
|
||||
// Ensures that even changing the default output directory devel_mail_log
|
||||
// works as expected.
|
||||
$changed_output_directory = 'temporary://my-folder';
|
||||
$expected_file_path = $changed_output_directory . '/' . $expected_filename;
|
||||
$this->config('devel.settings')
|
||||
->set('debug_mail_directory', $changed_output_directory)
|
||||
->save();
|
||||
|
||||
$result = $this->mailManager->mail($mail['module'], $mail['key'], $mail['to'], $mail['lang'], $params, $mail['reply']);
|
||||
$this->assertTrue($result['result']);
|
||||
$this->assertFileExists($expected_file_path);
|
||||
$this->assertStringEqualsFile($expected_file_path, $expected_output);
|
||||
|
||||
// Ensures that if the default output directory is a public directory it
|
||||
// will be protected by adding an .htaccess.
|
||||
$public_output_directory = 'public://my-folder';
|
||||
$expected_file_path = $public_output_directory . '/' . $expected_filename;
|
||||
$this->config('devel.settings')
|
||||
->set('debug_mail_directory', $public_output_directory)
|
||||
->save();
|
||||
|
||||
$this->mailManager->mail($mail['module'], $mail['key'], $mail['to'], $mail['lang'], $params, $mail['reply']);
|
||||
$this->assertFileExists($expected_file_path);
|
||||
$this->assertStringEqualsFile($expected_file_path, $expected_output);
|
||||
$this->assertFileExists($public_output_directory . '/.htaccess');
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure devel_mail_log as default mail backend.
|
||||
*/
|
||||
private function setDevelMailLogAsDefaultBackend(): void {
|
||||
// @todo can this be avoided?
|
||||
// KernelTestBase enforce the usage of 'test_mail_collector' plugin for
|
||||
// collect the mails. Since we need to test devel mail plugin we manually
|
||||
// configure the mail implementation to use 'devel_mail_log'.
|
||||
$GLOBALS['config']['system.mail']['interface']['default'] = 'devel_mail_log';
|
||||
|
||||
// Configure devel_mail_log as default mail backend.
|
||||
$this->config('system.mail')
|
||||
->set('interface.default', 'devel_mail_log')
|
||||
->save();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Kernel;
|
||||
|
||||
use Drupal\Core\Messenger\MessengerTrait;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests query debug.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelQueryDebugTest extends KernelTestBase {
|
||||
|
||||
use MessengerTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['devel', 'system', 'user'];
|
||||
|
||||
/**
|
||||
* The user used in test.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $develUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', 'sequences');
|
||||
$this->installConfig(['system', 'devel']);
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
$devel_role = Role::create([
|
||||
'id' => 'admin',
|
||||
'label' => 'Admin',
|
||||
'permissions' => ['access devel information'],
|
||||
]);
|
||||
$devel_role->save();
|
||||
|
||||
$this->develUser = User::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'roles' => [$devel_role->id()],
|
||||
]);
|
||||
$this->develUser->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel_query_debug_alter() for select queries.
|
||||
*/
|
||||
public function testSelectQueryDebugTag(): void {
|
||||
// Clear the messages stack.
|
||||
$this->getDrupalMessages();
|
||||
|
||||
// Ensures that no debug message is shown to user without the adequate
|
||||
// permissions.
|
||||
$query = \Drupal::database()->select('users', 'u');
|
||||
$query->fields('u', ['uid']);
|
||||
$query->addTag('debug');
|
||||
$query->execute();
|
||||
|
||||
$messages = $this->getDrupalMessages();
|
||||
$this->assertEmpty($messages);
|
||||
|
||||
// Ensures that the SQL debug message is shown to user with the adequate
|
||||
// permissions. We expect only one status message containing the SQL for
|
||||
// the debugged query.
|
||||
\Drupal::currentUser()->setAccount($this->develUser);
|
||||
$expected_message = '
|
||||
SELECT u.uid AS uid\\n
|
||||
FROM\\n
|
||||
{users} u
|
||||
|
||||
';
|
||||
|
||||
$query = \Drupal::database()->select('users', 'u');
|
||||
$query->fields('u', ['uid']);
|
||||
$query->addTag('debug');
|
||||
$query->execute();
|
||||
|
||||
$messages = $this->getDrupalMessages();
|
||||
$this->assertNotEmpty($messages['status']);
|
||||
$this->assertCount(1, $messages['status']);
|
||||
$actual_message = strip_tags($messages['status'][0]);
|
||||
// In Drupal 9 the literals are quoted, but not in Drupal 8. We only need
|
||||
// the actual content, so remove all quotes from the actual message found.
|
||||
$actual_message = str_replace(['"', "'"], ['', ''], $actual_message);
|
||||
$this->assertEquals($expected_message, $actual_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel_query_debug_alter() for entity queries.
|
||||
*/
|
||||
public function testEntityQueryDebugTag(): void {
|
||||
// Clear the messages stack.
|
||||
$this->getDrupalMessages();
|
||||
|
||||
// Ensures that no debug message is shown to user without the adequate
|
||||
// permissions.
|
||||
$query = \Drupal::entityQuery('user')->accessCheck(FALSE);
|
||||
$query->addTag('debug');
|
||||
$query->execute();
|
||||
|
||||
$messages = $this->getDrupalMessages();
|
||||
$this->assertEmpty($messages);
|
||||
|
||||
// Ensures that the SQL debug message is shown to user with the adequate
|
||||
// permissions. We expect only one status message containing the SQL for
|
||||
// the debugged entity query.
|
||||
\Drupal::currentUser()->setAccount($this->develUser);
|
||||
$expected_message = '
|
||||
SELECT base_table.uid AS uid, base_table.uid AS base_table_uid\\n
|
||||
FROM\\n
|
||||
{users} base_table
|
||||
|
||||
';
|
||||
|
||||
$query = \Drupal::entityQuery('user')->accessCheck(FALSE);
|
||||
$query->addTag('debug');
|
||||
$query->execute();
|
||||
|
||||
$messages = $this->getDrupalMessages();
|
||||
$this->assertNotEmpty($messages['status']);
|
||||
$this->assertCount(1, $messages['status']);
|
||||
$actual_message = strip_tags($messages['status'][0]);
|
||||
$actual_message = str_replace(['"', "'"], ['', ''], $actual_message);
|
||||
$this->assertEquals($expected_message, $actual_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves and removes the drupal messages.
|
||||
*
|
||||
* @return array
|
||||
* The messages
|
||||
*/
|
||||
protected function getDrupalMessages() {
|
||||
return $this->messenger()->deleteAll();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Kernel;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Messenger\MessengerTrait;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\devel\Twig\Extension\Debug;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests Twig extensions.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelTwigExtensionTest extends KernelTestBase {
|
||||
|
||||
use DevelDumperTestTrait;
|
||||
use MessengerTrait;
|
||||
|
||||
/**
|
||||
* The user used in test.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $develUser;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $modules = ['devel', 'user', 'system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('user');
|
||||
$this->installSchema('system', 'sequences');
|
||||
|
||||
$devel_role = Role::create([
|
||||
'id' => 'admin',
|
||||
'label' => 'Admin',
|
||||
'permissions' => ['access devel information'],
|
||||
]);
|
||||
$devel_role->save();
|
||||
|
||||
$this->develUser = User::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'roles' => [$devel_role->id()],
|
||||
]);
|
||||
$this->develUser->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register(ContainerBuilder $container): void {
|
||||
parent::register($container);
|
||||
|
||||
$parameters = $container->getParameter('twig.config');
|
||||
$parameters['debug'] = TRUE;
|
||||
$container->setParameter('twig.config', $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that Twig extension loads appropriately.
|
||||
*/
|
||||
public function testTwigExtensionLoaded(): void {
|
||||
$twig_service = \Drupal::service('twig');
|
||||
$extension = $twig_service->getExtension(Debug::class);
|
||||
$this->assertEquals($extension::class, Debug::class, 'Debug Extension loaded successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the Twig dump functions are registered properly.
|
||||
*/
|
||||
public function testDumpFunctionsRegistered(): void {
|
||||
/** @var \Drupal\Core\Template\TwigEnvironment $environment */
|
||||
$environment = $this->container->get('twig');
|
||||
$functions = $environment->getFunctions();
|
||||
|
||||
$dump_functions = ['devel_dump'];
|
||||
$message_functions = ['devel_message', 'dpm', 'dsm'];
|
||||
$registered_functions = $dump_functions + $message_functions;
|
||||
foreach ($registered_functions as $name) {
|
||||
$this->assertArrayHasKey($name, $functions);
|
||||
$function = $functions[$name];
|
||||
$this->assertEquals($function->getName(), $name);
|
||||
$this->assertTrue($function->needsContext());
|
||||
$this->assertTrue($function->needsEnvironment());
|
||||
$this->assertTrue($function->isVariadic());
|
||||
|
||||
is_callable($function->getCallable(), TRUE, $callable);
|
||||
if (in_array($name, $dump_functions)) {
|
||||
$this->assertEquals($callable, Debug::class . '::dump');
|
||||
}
|
||||
else {
|
||||
$this->assertEquals($callable, Debug::class . '::message');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the Twig function for XDebug integration is registered properly.
|
||||
*/
|
||||
public function testXdebugIntegrationFunctionsRegistered(): void {
|
||||
/** @var \Drupal\Core\Template\TwigEnvironment $environment */
|
||||
$environment = $this->container->get('twig');
|
||||
$function = $environment->getFunction('devel_breakpoint');
|
||||
$this->assertNotNull($function);
|
||||
$this->assertTrue($function->needsContext());
|
||||
$this->assertTrue($function->needsEnvironment());
|
||||
$this->assertTrue($function->isVariadic());
|
||||
is_callable($function->getCallable(), TRUE, $callable);
|
||||
$this->assertEquals($callable, Debug::class . '::breakpoint');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the Twig extension's dump functions produce the expected output.
|
||||
*/
|
||||
public function testDumpFunctions(): void {
|
||||
$template = 'test-with-context {{ twig_string }} {{ twig_array.first }} {{ twig_array.second }}{{ devel_dump() }}';
|
||||
$expected_template_output = 'test-with-context context! first value second value';
|
||||
|
||||
$context = [
|
||||
'twig_string' => 'context!',
|
||||
'twig_array' => [
|
||||
'first' => 'first value',
|
||||
'second' => 'second value',
|
||||
],
|
||||
'twig_object' => new \stdClass(),
|
||||
];
|
||||
|
||||
/** @var \Drupal\Core\Template\TwigEnvironment $environment */
|
||||
$environment = \Drupal::service('twig');
|
||||
|
||||
// Ensures that the twig extension does nothing if the current
|
||||
// user has not the adequate permission.
|
||||
$this->assertTrue($environment->isDebug());
|
||||
$this->assertEquals($environment->renderInline($template, $context), $expected_template_output);
|
||||
|
||||
\Drupal::currentUser()->setAccount($this->develUser);
|
||||
|
||||
// Ensures that if no argument is passed to the function the twig context is
|
||||
// dumped.
|
||||
$output = (string) $environment->renderInline($template, $context);
|
||||
$this->assertStringContainsString($expected_template_output, $output, 'When no argument passed');
|
||||
$this->assertContainsDump($output, $context, 'Twig context');
|
||||
|
||||
// Ensures that if an argument is passed to the function it is dumped.
|
||||
$template = 'test-with-context {{ twig_string }} {{ twig_array.first }} {{ twig_array.second }}{{ devel_dump(twig_array) }}';
|
||||
$output = (string) $environment->renderInline($template, $context);
|
||||
$this->assertStringContainsString($expected_template_output, $output, 'When one argument is passed');
|
||||
$this->assertContainsDump($output, $context['twig_array']);
|
||||
|
||||
// Ensures that if more than one argument is passed the function works
|
||||
// properly and every argument is dumped separately.
|
||||
$template = 'test-with-context {{ twig_string }} {{ twig_array.first }} {{ twig_array.second }}{{ devel_dump(twig_string, twig_array.first, twig_array, twig_object) }}';
|
||||
$output = (string) $environment->renderInline($template, $context);
|
||||
$this->assertStringContainsString($expected_template_output, $output, 'When multiple arguments are passed');
|
||||
$this->assertContainsDump($output, $context['twig_string']);
|
||||
$this->assertContainsDump($output, $context['twig_array']['first']);
|
||||
$this->assertContainsDump($output, $context['twig_array']);
|
||||
$this->assertContainsDump($output, $context['twig_object']);
|
||||
|
||||
// Clear messages.
|
||||
$this->messenger()->deleteAll();
|
||||
|
||||
$retrieve_message = static fn($messages, $index): ?string => isset($messages['status'][$index]) ? (string) $messages['status'][$index] : NULL;
|
||||
|
||||
// Ensures that if no argument is passed to the function the twig context is
|
||||
// dumped.
|
||||
$template = 'test-with-context {{ twig_string }} {{ twig_array.first }} {{ twig_array.second }}{{ devel_message() }}';
|
||||
$output = (string) $environment->renderInline($template, $context);
|
||||
$this->assertStringContainsString($expected_template_output, $output, 'When no argument passed');
|
||||
$messages = \Drupal::messenger()->deleteAll();
|
||||
$this->assertDumpExportEquals($retrieve_message($messages, 0), $context, 'Twig context');
|
||||
|
||||
// Ensures that if an argument is passed to the function it is dumped.
|
||||
$template = 'test-with-context {{ twig_string }} {{ twig_array.first }} {{ twig_array.second }}{{ devel_message(twig_array) }}';
|
||||
$output = (string) $environment->renderInline($template, $context);
|
||||
$this->assertStringContainsString($expected_template_output, $output, 'When one argument is passed');
|
||||
$messages = $this->messenger()->deleteAll();
|
||||
$this->assertDumpExportEquals($retrieve_message($messages, 0), $context['twig_array']);
|
||||
|
||||
// Ensures that if more than one argument is passed to the function works
|
||||
// properly and every argument is dumped separately.
|
||||
$template = 'test-with-context {{ twig_string }} {{ twig_array.first }} {{ twig_array.second }}{{ devel_message(twig_string, twig_array.first, twig_array, twig_object) }}';
|
||||
$output = (string) $environment->renderInline($template, $context);
|
||||
$this->assertStringContainsString($expected_template_output, $output, 'When multiple arguments are passed');
|
||||
$messages = $this->messenger()->deleteAll();
|
||||
$this->assertDumpExportEquals($retrieve_message($messages, 0), $context['twig_string']);
|
||||
$this->assertDumpExportEquals($retrieve_message($messages, 1), $context['twig_array']['first']);
|
||||
$this->assertDumpExportEquals($retrieve_message($messages, 2), $context['twig_array']);
|
||||
$this->assertDumpExportEquals($retrieve_message($messages, 3), $context['twig_object']);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Unit;
|
||||
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\devel\Element\ClientSideFilterTable;
|
||||
|
||||
/**
|
||||
* Tests ClientSideFilterTable element.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\devel\Element\ClientSideFilterTable
|
||||
* @group devel
|
||||
*/
|
||||
class DevelClientSideFilterTableTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::getInfo
|
||||
*/
|
||||
public function testGetInfo(): void {
|
||||
$translation = $this->getStringTranslationStub();
|
||||
|
||||
$expected_info = [
|
||||
'#filter_label' => $translation->translate('Search'),
|
||||
'#filter_placeholder' => $translation->translate('Search'),
|
||||
'#filter_description' => $translation->translate('Search'),
|
||||
'#header' => [],
|
||||
'#rows' => [],
|
||||
'#empty' => '',
|
||||
'#sticky' => FALSE,
|
||||
'#responsive' => TRUE,
|
||||
'#attributes' => [],
|
||||
'#pre_render' => [
|
||||
[ClientSideFilterTable::class, 'preRenderTable'],
|
||||
],
|
||||
];
|
||||
|
||||
$table = new ClientSideFilterTable([], 'test', 'test');
|
||||
$table->setStringTranslation($translation);
|
||||
$this->assertEquals($expected_info, $table->getInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::preRenderTable
|
||||
* @dataProvider providerPreRenderTable
|
||||
*/
|
||||
public function testPreRenderTable($element, $expected): void {
|
||||
$result = ClientSideFilterTable::preRenderTable($element);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for preRenderHtmlTag test.
|
||||
*/
|
||||
public static function providerPreRenderTable(): array {
|
||||
$data = [];
|
||||
$filter_label = new TranslatableMarkup('Label 1');
|
||||
$filter_label_2 = new TranslatableMarkup('Label 2');
|
||||
$filter_placeholder = new TranslatableMarkup('Placeholder 1');
|
||||
$filter_placeholder_2 = new TranslatableMarkup('Placeholder 2');
|
||||
$filter_description = new TranslatableMarkup('Description 1');
|
||||
$filter_description_2 = new TranslatableMarkup('Description 2');
|
||||
$empty = new TranslatableMarkup('Empty');
|
||||
$empty_2 = new TranslatableMarkup('Empty 2');
|
||||
$actual = [
|
||||
'#type' => 'devel_table_filter',
|
||||
'#filter_label' => $filter_label,
|
||||
'#filter_placeholder' => $filter_placeholder,
|
||||
'#filter_description' => $filter_description,
|
||||
'#header' => [],
|
||||
'#rows' => [],
|
||||
'#empty' => $empty,
|
||||
'#responsive' => TRUE,
|
||||
'#sticky' => TRUE,
|
||||
'#attributes' => [
|
||||
'class' => ['devel-a-list'],
|
||||
],
|
||||
];
|
||||
|
||||
$expected = [];
|
||||
$expected['#attached']['library'][] = 'devel/devel-table-filter';
|
||||
$expected['filters'] = [
|
||||
'#type' => 'container',
|
||||
'#weight' => -1,
|
||||
'#attributes' => ['class' => ['table-filter', 'js-show']],
|
||||
'name' => [
|
||||
'#type' => 'search',
|
||||
'#size' => 30,
|
||||
'#title' => $filter_label,
|
||||
'#placeholder' => $filter_placeholder,
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter-text'],
|
||||
'data-table' => ".js-devel-table-filter",
|
||||
'autocomplete' => 'off',
|
||||
'title' => $filter_description,
|
||||
],
|
||||
],
|
||||
];
|
||||
$expected['table'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => [],
|
||||
'#rows' => [],
|
||||
'#empty' => $empty,
|
||||
'#responsive' => TRUE,
|
||||
'#sticky' => TRUE,
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
'devel-a-list',
|
||||
'js-devel-table-filter',
|
||||
'devel-table-filter',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$data[] = [$actual, $expected];
|
||||
|
||||
$headers = ['Test1', 'Test2', 'Test3', 'Test4', 'Test5'];
|
||||
|
||||
$actual = [
|
||||
'#type' => 'devel_table_filter',
|
||||
'#filter_label' => $filter_label_2,
|
||||
'#filter_placeholder' => $filter_placeholder_2,
|
||||
'#filter_description' => $filter_description_2,
|
||||
'#header' => $headers,
|
||||
'#rows' => [
|
||||
[
|
||||
['data' => 'test1', 'filter' => TRUE],
|
||||
['data' => 'test2', 'filter' => TRUE, 'class' => ['test2']],
|
||||
['data' => 'test3', 'class' => ['test3']],
|
||||
['test4'],
|
||||
[
|
||||
'data' => 'test5',
|
||||
'filter' => TRUE,
|
||||
'class' => ['devel-event-name-header'],
|
||||
'colspan' => '3',
|
||||
'header' => TRUE,
|
||||
],
|
||||
],
|
||||
],
|
||||
'#empty' => $empty_2,
|
||||
'#responsive' => FALSE,
|
||||
'#sticky' => FALSE,
|
||||
'#attributes' => [
|
||||
'class' => ['devel-some-list'],
|
||||
],
|
||||
];
|
||||
|
||||
$expected = [];
|
||||
$expected['#attached']['library'][] = 'devel/devel-table-filter';
|
||||
$expected['filters'] = [
|
||||
'#type' => 'container',
|
||||
'#weight' => -1,
|
||||
'#attributes' => ['class' => ['table-filter', 'js-show']],
|
||||
'name' => [
|
||||
'#type' => 'search',
|
||||
'#size' => 30,
|
||||
'#title' => $filter_label_2,
|
||||
'#placeholder' => $filter_placeholder_2,
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter-text'],
|
||||
'data-table' => ".js-devel-table-filter--2",
|
||||
'autocomplete' => 'off',
|
||||
'title' => $filter_description_2,
|
||||
],
|
||||
],
|
||||
];
|
||||
$expected['table'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $headers,
|
||||
'#rows' => [
|
||||
[
|
||||
[
|
||||
'data' => 'test1',
|
||||
'filter' => TRUE,
|
||||
'class' => ['table-filter-text-source'],
|
||||
],
|
||||
[
|
||||
'data' => 'test2',
|
||||
'filter' => TRUE,
|
||||
'class' => ['test2', 'table-filter-text-source'],
|
||||
],
|
||||
['data' => 'test3', 'class' => ['test3']],
|
||||
['test4'],
|
||||
[
|
||||
'data' => 'test5',
|
||||
'filter' => TRUE,
|
||||
'class' => ['devel-event-name-header', 'table-filter-text-source'],
|
||||
'colspan' => '3',
|
||||
'header' => TRUE,
|
||||
],
|
||||
],
|
||||
],
|
||||
'#empty' => $empty_2,
|
||||
'#responsive' => FALSE,
|
||||
'#sticky' => FALSE,
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
'devel-some-list',
|
||||
'js-devel-table-filter--2',
|
||||
'devel-table-filter',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$data[] = [$actual, $expected];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user