ViewsModerationStateFilterTest.php
Same filename in this branch
Same filename in other branches
- 8.9.x core/modules/content_moderation/tests/src/Kernel/ViewsModerationStateFilterTest.php
- 8.9.x core/modules/content_moderation/tests/src/Functional/ViewsModerationStateFilterTest.php
- 10 core/modules/content_moderation/tests/src/Kernel/ViewsModerationStateFilterTest.php
- 10 core/modules/content_moderation/tests/src/Functional/ViewsModerationStateFilterTest.php
- 11.x core/modules/content_moderation/tests/src/Kernel/ViewsModerationStateFilterTest.php
- 11.x core/modules/content_moderation/tests/src/Functional/ViewsModerationStateFilterTest.php
Namespace
Drupal\Tests\content_moderation\FunctionalFile
-
core/
modules/ content_moderation/ tests/ src/ Functional/ ViewsModerationStateFilterTest.php
View source
<?php
namespace Drupal\Tests\content_moderation\Functional;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Entity\View;
use Drupal\views\ViewEntityInterface;
use Drupal\workflows\Entity\Workflow;
/**
* Tests the views 'moderation_state_filter' filter plugin.
*
* @coversDefaultClass \Drupal\content_moderation\Plugin\views\filter\ModerationStateFilter
*
* @group content_moderation
*/
class ViewsModerationStateFilterTest extends ViewTestBase {
use ContentModerationTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'node',
'content_moderation',
'workflows',
'workflow_type_test',
'entity_test',
'language',
'content_translation',
'views_ui',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = []) : void {
parent::setUp(FALSE, $modules);
NodeType::create([
'type' => 'example_a',
])->save();
NodeType::create([
'type' => 'example_b',
])->save();
NodeType::create([
'type' => 'example_c',
])->save();
$this->createEditorialWorkflow();
$new_workflow = Workflow::create([
'type' => 'content_moderation',
'id' => 'new_workflow',
'label' => 'New workflow',
]);
$new_workflow->getTypePlugin()
->addState('bar', 'Bar');
$new_workflow->getTypePlugin()
->addEntityTypeAndBundle('node', 'example_c');
$new_workflow->save();
$this->drupalLogin($this->drupalCreateUser([
'administer workflows',
'administer views',
]));
$this->container
->get('module_installer')
->install([
'content_moderation_test_views',
]);
$new_workflow->getTypePlugin()
->removeEntityTypeAndBundle('node', 'example_c');
$new_workflow->save();
}
/**
* Tests the dependency handling of the moderation state filter.
*
* @covers ::calculateDependencies
* @covers ::onDependencyRemoval
*/
public function testModerationStateFilterDependencyHandling() {
// First, check that the view doesn't have any config dependency when there
// are no states configured in the filter.
$view_id = 'test_content_moderation_state_filter_base_table';
$view = View::load($view_id);
$this->assertWorkflowDependencies([], $view);
$this->assertTrue($view->status());
// Configure the Editorial workflow for a node bundle, set the filter value
// to use one of its states and check that the workflow is now a dependency
// of the view.
$this->drupalGet('admin/config/workflow/workflows/manage/editorial/type/node');
$this->submitForm([
'bundles[example_a]' => TRUE,
], 'Save');
$edit['options[value][]'] = [
'editorial-published',
];
$this->drupalGet("admin/structure/views/nojs/handler/{$view_id}/default/filter/moderation_state");
$this->submitForm($edit, 'Apply');
$this->drupalGet("admin/structure/views/view/{$view_id}");
$this->submitForm([], 'Save');
$view = $this->loadViewUnchanged($view_id);
$this->assertWorkflowDependencies([
'editorial',
], $view);
$this->assertTrue($view->status());
// Create another workflow and repeat the checks above.
$this->drupalGet('admin/config/workflow/workflows/add');
$this->submitForm([
'label' => 'Translation',
'id' => 'translation',
'workflow_type' => 'content_moderation',
], 'Save');
$this->drupalGet('admin/config/workflow/workflows/manage/translation/add_state');
$this->submitForm([
'label' => 'Needs Review',
'id' => 'needs_review',
], 'Save');
$this->drupalGet('admin/config/workflow/workflows/manage/translation/type/node');
$this->submitForm([
'bundles[example_b]' => TRUE,
], 'Save');
$edit['options[value][]'] = [
'editorial-published',
'translation-needs_review',
];
$this->drupalGet("admin/structure/views/nojs/handler/{$view_id}/default/filter/moderation_state");
$this->submitForm($edit, 'Apply');
$this->drupalGet("admin/structure/views/view/{$view_id}");
$this->submitForm([], 'Save');
$view = $this->loadViewUnchanged($view_id);
$this->assertWorkflowDependencies([
'editorial',
'translation',
], $view);
$this->assertTrue(isset($view->getDisplay('default')['display_options']['filters']['moderation_state']));
$this->assertTrue($view->status());
// Remove the 'Translation' workflow.
$this->drupalGet('admin/config/workflow/workflows/manage/translation/delete');
$this->submitForm([], 'Delete');
// Check that the view has been disabled, the filter has been deleted, the
// view can be saved and there are no more config dependencies.
$view = $this->loadViewUnchanged($view_id);
$this->assertFalse($view->status());
$this->assertFalse(isset($view->getDisplay('default')['display_options']['filters']['moderation_state']));
$this->drupalGet("admin/structure/views/view/{$view_id}");
$this->submitForm([], 'Save');
$this->assertWorkflowDependencies([], $view);
}
/**
* Load a view from the database after it has been modified in a sub-request.
*
* @param string $view_id
* The view ID.
*
* @return \Drupal\views\ViewEntityInterface
* A loaded view, bypassing static caches.
*/
public function loadViewUnchanged($view_id) {
$this->container
->get('cache.config')
->deleteAll();
$this->container
->get('config.factory')
->reset();
return $this->container
->get('entity_type.manager')
->getStorage('view')
->loadUnchanged($view_id);
}
/**
* Tests the moderation state filter when the configured workflow is changed.
*
* @dataProvider providerTestWorkflowChanges
*/
public function testWorkflowChanges($view_id) {
// First, apply the Editorial workflow to both of our content types.
$this->drupalGet('admin/config/workflow/workflows/manage/editorial/type/node');
$this->submitForm([
'bundles[example_a]' => TRUE,
'bundles[example_b]' => TRUE,
], 'Save');
\Drupal::service('entity_type.bundle.info')->clearCachedBundles();
// Update the view and make the default filter not exposed anymore,
// otherwise all results will be shown when there are no more moderated
// bundles left.
$this->drupalGet("admin/structure/views/nojs/handler/{$view_id}/default/filter/moderation_state");
$this->submitForm([], 'Hide filter');
$this->drupalGet("admin/structure/views/view/{$view_id}");
$this->submitForm([], 'Save');
// Add a few nodes in various moderation states.
$this->createNode([
'type' => 'example_a',
'moderation_state' => 'published',
]);
$this->createNode([
'type' => 'example_b',
'moderation_state' => 'published',
]);
$archived_node_a = $this->createNode([
'type' => 'example_a',
'moderation_state' => 'archived',
]);
$archived_node_b = $this->createNode([
'type' => 'example_b',
'moderation_state' => 'archived',
]);
// Configure the view to only show nodes in the 'archived' moderation state.
$edit['options[value][]'] = [
'editorial-archived',
];
$this->drupalGet("admin/structure/views/nojs/handler/{$view_id}/default/filter/moderation_state");
$this->submitForm($edit, 'Apply');
$this->drupalGet("admin/structure/views/view/{$view_id}");
$this->submitForm([], 'Save');
// Check that only the archived nodes from both bundles are displayed by the
// view.
$view = $this->loadViewUnchanged($view_id);
$this->executeAndAssertIdenticalResultset($view, [
[
'nid' => $archived_node_a->id(),
],
[
'nid' => $archived_node_b->id(),
],
], [
'nid' => 'nid',
]);
// Remove the Editorial workflow from one of the bundles.
$this->drupalGet('admin/config/workflow/workflows/manage/editorial/type/node');
$this->submitForm([
'bundles[example_a]' => TRUE,
'bundles[example_b]' => FALSE,
], 'Save');
\Drupal::service('entity_type.bundle.info')->clearCachedBundles();
$view = $this->loadViewUnchanged($view_id);
$this->executeAndAssertIdenticalResultset($view, [
[
'nid' => $archived_node_a->id(),
],
], [
'nid' => 'nid',
]);
// Check that the view can still be edited and saved without any
// intervention.
$this->drupalGet("admin/structure/views/view/{$view_id}");
$this->submitForm([], 'Save');
// Remove the Editorial workflow from both bundles.
$this->drupalGet('admin/config/workflow/workflows/manage/editorial/type/node');
$this->submitForm([
'bundles[example_a]' => FALSE,
'bundles[example_b]' => FALSE,
], 'Save');
\Drupal::service('entity_type.bundle.info')->clearCachedBundles();
// Check that the view doesn't return any result.
$view = $this->loadViewUnchanged($view_id);
$this->executeAndAssertIdenticalResultset($view, [], []);
// Check that the view contains a broken filter, since the moderation_state
// field was removed from the entity type.
$this->drupalGet("admin/structure/views/view/{$view_id}");
$this->submitForm([], 'Save');
$this->assertSession()
->pageTextContains("Broken/missing handler");
}
/**
* Execute a view and assert the expected results.
*
* @param \Drupal\views\ViewEntityInterface $view_entity
* A view configuration entity.
* @param array $expected
* An expected result set.
* @param array $column_map
* An associative array mapping the columns of the result set from the view
* (as keys) and the expected result set (as values).
*/
protected function executeAndAssertIdenticalResultset(ViewEntityInterface $view_entity, $expected, $column_map) {
$executable = $this->container
->get('views.executable')
->get($view_entity);
$this->executeView($executable);
$this->assertIdenticalResultset($executable, $expected, $column_map);
}
/**
* Data provider for testWorkflowChanges.
*
* @return string[]
* An array of view IDs.
*/
public function providerTestWorkflowChanges() {
return [
'view on base table, filter on base table' => [
'test_content_moderation_state_filter_base_table',
],
'view on base table, filter on revision table' => [
'test_content_moderation_state_filter_base_table_filter_on_revision',
],
];
}
/**
* Tests the content moderation state filter caching is correct.
*/
public function testFilterRenderCache() {
// Initially all states of the workflow are displayed.
$this->drupalGet('admin/config/workflow/workflows/manage/editorial/type/node');
$this->submitForm([
'bundles[example_a]' => TRUE,
], 'Save');
$this->assertFilterStates([
'All',
'editorial-draft',
'editorial-published',
'editorial-archived',
]);
// Adding a new state to the editorial workflow will display that state in
// the list of filters.
$this->drupalGet('admin/config/workflow/workflows/manage/editorial/add_state');
$this->submitForm([
'label' => 'Foo',
'id' => 'foo',
], 'Save');
$this->assertFilterStates([
'All',
'editorial-draft',
'editorial-published',
'editorial-archived',
'editorial-foo',
]);
// Adding a second workflow to nodes will also show new states.
$this->drupalGet('admin/config/workflow/workflows/manage/new_workflow/type/node');
$this->submitForm([
'bundles[example_b]' => TRUE,
], 'Save');
$this->assertFilterStates([
'All',
'editorial-draft',
'editorial-published',
'editorial-archived',
'editorial-foo',
'new_workflow-draft',
'new_workflow-published',
'new_workflow-bar',
]);
// Add a few more states and change the exposed filter to allow multiple
// selections so we can check that the size of the select element does not
// exceed 8 options.
$this->drupalGet('admin/config/workflow/workflows/manage/editorial/add_state');
$this->submitForm([
'label' => 'Foo 2',
'id' => 'foo2',
], 'Save');
$this->drupalGet('admin/config/workflow/workflows/manage/editorial/add_state');
$this->submitForm([
'label' => 'Foo 3',
'id' => 'foo3',
], 'Save');
$view_id = 'test_content_moderation_state_filter_base_table';
$edit['options[expose][multiple]'] = TRUE;
$this->drupalGet("admin/structure/views/nojs/handler/{$view_id}/default/filter/moderation_state");
$this->submitForm($edit, 'Apply');
$this->drupalGet("admin/structure/views/view/{$view_id}");
$this->submitForm([], 'Save');
$this->assertFilterStates([
'editorial-draft',
'editorial-published',
'editorial-archived',
'editorial-foo',
'editorial-foo2',
'editorial-foo3',
'new_workflow-draft',
'new_workflow-published',
'new_workflow-bar',
], TRUE);
}
/**
* Assert the states which appear in the filter.
*
* @param array $states
* The states which should appear in the filter.
* @param bool $check_size
* (optional) Whether to check that size of the select element is not
* greater than 8. Defaults to FALSE.
*
* @internal
*/
protected function assertFilterStates(array $states, bool $check_size = FALSE) : void {
$this->drupalGet('/filter-test-path');
$assert_session = $this->assertSession();
// Check that the select contains the correct number of options.
$assert_session->elementsCount('css', '#edit-default-revision-state option', count($states));
// Check that the size of the select element does not exceed 8 options.
if ($check_size) {
$this->assertGreaterThan(8, count($states));
$assert_session->elementAttributeContains('css', '#edit-default-revision-state', 'size', 8);
}
// Check that an option exists for each of the expected states.
foreach ($states as $state) {
$assert_session->optionExists('Default Revision State', $state);
}
}
/**
* Asserts the views dependencies on workflow config entities.
*
* @param string[] $workflow_ids
* An array of workflow IDs to check.
* @param \Drupal\views\ViewEntityInterface $view
* A view configuration object.
*
* @internal
*/
protected function assertWorkflowDependencies(array $workflow_ids, ViewEntityInterface $view) : void {
$dependencies = $view->getDependencies();
$expected = [];
foreach (Workflow::loadMultiple($workflow_ids) as $workflow) {
$expected[] = $workflow->getConfigDependencyName();
}
if ($expected) {
$this->assertSame($expected, $dependencies['config']);
}
else {
$this->assertTrue(!isset($dependencies['config']));
}
}
}
Classes
Title | Deprecated | Summary |
---|---|---|
ViewsModerationStateFilterTest | Tests the views 'moderation_state_filter' filter plugin. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.