3 namespace Drupal\Tests\content_moderation\Functional;
5 use Drupal\node\Entity\NodeType;
6 use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
7 use Drupal\Tests\views\Functional\ViewTestBase;
8 use Drupal\views\ViewExecutable;
9 use Drupal\views\Views;
10 use Drupal\workflows\Entity\Workflow;
13 * Tests the views 'moderation_state_filter' filter plugin.
15 * @coversDefaultClass \Drupal\content_moderation\Plugin\views\filter\ModerationStateFilter
17 * @group content_moderation
19 class ViewsModerationStateFilterTest extends ViewTestBase {
21 use ContentModerationTestTrait;
26 public static $modules = [
27 'content_moderation_test_views',
34 'content_translation',
41 protected function setUp($import_test_views = TRUE) {
45 'type' => 'example_a',
48 'type' => 'example_b',
51 $this->createEditorialWorkflow();
53 $new_workflow = Workflow::create([
54 'type' => 'content_moderation',
55 'id' => 'new_workflow',
56 'label' => 'New workflow',
58 $new_workflow->getTypePlugin()->addState('bar', 'Bar');
59 $new_workflow->save();
61 $this->drupalLogin($this->drupalCreateUser(['administer workflows', 'administer views']));
65 * Tests the dependency handling of the moderation state filter.
67 * @covers ::calculateDependencies
68 * @covers ::onDependencyRemoval
70 public function testModerationStateFilterDependencyHandling() {
71 // First, check that the view doesn't have any config dependency when there
72 // are no states configured in the filter.
73 $view_id = 'test_content_moderation_state_filter_base_table';
74 $view = Views::getView($view_id);
76 $this->assertWorkflowDependencies([], $view);
77 $this->assertTrue($view->storage->status());
79 // Configure the Editorial workflow for a node bundle, set the filter value
80 // to use one of its states and check that the workflow is now a dependency
82 $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/type/node', [
83 'bundles[example_a]' => TRUE,
86 $edit['options[value][]'] = ['editorial-published'];
87 $this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", $edit, 'Apply');
88 $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
90 $view = Views::getView($view_id);
91 $this->assertWorkflowDependencies(['editorial'], $view);
92 $this->assertTrue($view->storage->status());
94 // Create another workflow and repeat the checks above.
95 $this->drupalPostForm('admin/config/workflow/workflows/add', [
96 'label' => 'Translation',
97 'id' => 'translation',
98 'workflow_type' => 'content_moderation',
100 $this->drupalPostForm('admin/config/workflow/workflows/manage/translation/add_state', [
101 'label' => 'Needs Review',
102 'id' => 'needs_review',
104 $this->drupalPostForm('admin/config/workflow/workflows/manage/translation/type/node', [
105 'bundles[example_b]' => TRUE,
108 $edit['options[value][]'] = ['editorial-published', 'translation-needs_review'];
109 $this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", $edit, 'Apply');
110 $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
112 $view = Views::getView($view_id);
113 $this->assertWorkflowDependencies(['editorial', 'translation'], $view);
114 $this->assertTrue(isset($view->storage->getDisplay('default')['display_options']['filters']['moderation_state']));
115 $this->assertTrue($view->storage->status());
117 // Remove the 'Translation' workflow.
118 $this->drupalPostForm('admin/config/workflow/workflows/manage/translation/delete', [], 'Delete');
120 // Check that the view has been disabled, the filter has been deleted, the
121 // view can be saved and there are no more config dependencies.
122 $view = Views::getView($view_id);
123 $this->assertFalse($view->storage->status());
124 $this->assertFalse(isset($view->storage->getDisplay('default')['display_options']['filters']['moderation_state']));
125 $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
126 $this->assertWorkflowDependencies([], $view);
130 * Tests the moderation state filter when the configured workflow is changed.
132 * @dataProvider providerTestWorkflowChanges
134 public function testWorkflowChanges($view_id, $filter_name) {
135 // Update the view and make the default filter not exposed anymore,
136 // otherwise all results will be shown when there are no more moderated
138 $this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", [], 'Hide filter');
139 $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
141 // First, apply the Editorial workflow to both of our content types.
142 $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/type/node', [
143 'bundles[example_a]' => TRUE,
144 'bundles[example_b]' => TRUE,
146 \Drupal::service('entity_type.bundle.info')->clearCachedBundles();
148 // Add a few nodes in various moderation states.
149 $this->createNode(['type' => 'example_a', 'moderation_state' => 'published']);
150 $this->createNode(['type' => 'example_b', 'moderation_state' => 'published']);
151 $archived_node_a = $this->createNode(['type' => 'example_a', 'moderation_state' => 'archived']);
152 $archived_node_b = $this->createNode(['type' => 'example_b', 'moderation_state' => 'archived']);
154 // Configure the view to only show nodes in the 'archived' moderation state.
155 $edit['options[value][]'] = ['editorial-archived'];
156 $this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", $edit, 'Apply');
157 $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
159 // Check that only the archived nodes from both bundles are displayed by the
161 $view = Views::getView($view_id);
162 $this->executeView($view);
163 $this->assertIdenticalResultset($view, [['nid' => $archived_node_a->id()], ['nid' => $archived_node_b->id()]], ['nid' => 'nid']);
165 // Remove the Editorial workflow from one of the bundles.
166 $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/type/node', [
167 'bundles[example_a]' => TRUE,
168 'bundles[example_b]' => FALSE,
170 \Drupal::service('entity_type.bundle.info')->clearCachedBundles();
172 $view = Views::getView($view_id);
173 $this->executeView($view);
174 $this->assertIdenticalResultset($view, [['nid' => $archived_node_a->id()]], ['nid' => 'nid']);
176 // Check that the view can still be edited and saved without any
178 $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
180 // Remove the Editorial workflow from both bundles.
181 $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/type/node', [
182 'bundles[example_a]' => FALSE,
183 'bundles[example_b]' => FALSE,
185 \Drupal::service('entity_type.bundle.info')->clearCachedBundles();
187 $view = Views::getView($view_id);
188 $this->executeView($view);
190 // Check that the view doesn't return any result.
191 $this->assertEmpty($view->result);
193 // Check that the view can not be edited without any intervention anymore
194 // because the user needs to fix the filter.
195 $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
196 $this->assertSession()->pageTextContains("No valid values found on filter: $filter_name.");
200 * Data provider for testWorkflowChanges.
203 * An array of view IDs.
205 public function providerTestWorkflowChanges() {
207 'view on base table, filter on base table' => [
208 'test_content_moderation_state_filter_base_table',
209 'Content: Moderation state',
211 'view on base table, filter on revision table' => [
212 'test_content_moderation_state_filter_base_table_filter_on_revision',
213 'Content revision: Moderation state',
219 * Tests the content moderation state filter caching is correct.
221 public function testFilterRenderCache() {
222 // Initially all states of the workflow are displayed.
223 $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/type/node', [
224 'bundles[example_a]' => TRUE,
226 $this->assertFilterStates(['All', 'editorial-draft', 'editorial-published', 'editorial-archived']);
228 // Adding a new state to the editorial workflow will display that state in
229 // the list of filters.
230 $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/add_state', [
234 $this->assertFilterStates(['All', 'editorial-draft', 'editorial-published', 'editorial-archived', 'editorial-foo']);
236 // Adding a second workflow to nodes will also show new states.
237 $this->drupalPostForm('admin/config/workflow/workflows/manage/new_workflow/type/node', [
238 'bundles[example_b]' => TRUE,
240 $this->assertFilterStates(['All', 'editorial-draft', 'editorial-published', 'editorial-archived', 'editorial-foo', 'new_workflow-draft', 'new_workflow-published', 'new_workflow-bar']);
242 // Add a few more states and change the exposed filter to allow multiple
243 // selections so we can check that the size of the select element does not
245 $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/add_state', [
249 $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/add_state', [
254 $view_id = 'test_content_moderation_state_filter_base_table';
255 $edit['options[expose][multiple]'] = TRUE;
256 $this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", $edit, 'Apply');
257 $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
259 $this->assertFilterStates(['editorial-draft', 'editorial-published', 'editorial-archived', 'editorial-foo', 'editorial-foo2', 'editorial-foo3', 'new_workflow-draft', 'new_workflow-published', 'new_workflow-bar'], TRUE);
263 * Assert the states which appear in the filter.
265 * @param array $states
266 * The states which should appear in the filter.
267 * @param bool $check_size
268 * (optional) Whether to check that size of the select element is not
269 * greater than 8. Defaults to FALSE.
271 protected function assertFilterStates($states, $check_size = FALSE) {
272 $this->drupalGet('/filter-test-path');
274 $assert_session = $this->assertSession();
276 // Check that the select contains the correct number of options.
277 $assert_session->elementsCount('css', '#edit-default-revision-state option', count($states));
279 // Check that the size of the select element does not exceed 8 options.
281 $this->assertGreaterThan(8, count($states));
282 $assert_session->elementAttributeContains('css', '#edit-default-revision-state', 'size', 8);
285 // Check that an option exists for each of the expected states.
286 foreach ($states as $state) {
287 $assert_session->optionExists('Default Revision State', $state);
292 * Asserts the views dependencies on workflow config entities.
294 * @param string[] $workflow_ids
295 * An array of workflow IDs to check.
296 * @param \Drupal\views\ViewExecutable $view
297 * An executable View object.
299 protected function assertWorkflowDependencies(array $workflow_ids, ViewExecutable $view) {
300 $dependencies = $view->getDependencies();
303 foreach (Workflow::loadMultiple($workflow_ids) as $workflow) {
304 $expected[] = $workflow->getConfigDependencyName();
308 $this->assertSame($expected, $dependencies['config']);
311 $this->assertTrue(!isset($dependencies['config']));