e5b296d7f45ab50fe5b2529bd4661d668798f42d
[yaffs-website] / web / core / modules / content_moderation / tests / src / Functional / ViewsModerationStateFilterTest.php
1 <?php
2
3 namespace Drupal\Tests\content_moderation\Functional;
4
5 use Drupal\node\Entity\NodeType;
6 use Drupal\Tests\views\Functional\ViewTestBase;
7 use Drupal\views\ViewExecutable;
8 use Drupal\views\Views;
9 use Drupal\workflows\Entity\Workflow;
10
11 /**
12  * Tests the views 'moderation_state_filter' filter plugin.
13  *
14  * @coversDefaultClass \Drupal\content_moderation\Plugin\views\filter\ModerationStateFilter
15  *
16  * @group content_moderation
17  */
18 class ViewsModerationStateFilterTest extends ViewTestBase {
19
20   /**
21    * {@inheritdoc}
22    */
23   public static $modules = [
24     'content_moderation_test_views',
25     'node',
26     'content_moderation',
27     'workflows',
28     'workflow_type_test',
29     'entity_test',
30     'language',
31     'content_translation',
32     'views_ui',
33   ];
34
35   /**
36    * {@inheritdoc}
37    */
38   protected function setUp($import_test_views = TRUE) {
39     parent::setUp(FALSE);
40
41     NodeType::create([
42       'type' => 'example_a',
43     ])->save();
44     NodeType::create([
45       'type' => 'example_b',
46     ])->save();
47
48     $new_workflow = Workflow::create([
49       'type' => 'content_moderation',
50       'id' => 'new_workflow',
51       'label' => 'New workflow',
52     ]);
53     $new_workflow->getTypePlugin()->addState('bar', 'Bar');
54     $new_workflow->save();
55
56     $this->drupalLogin($this->drupalCreateUser(['administer workflows', 'administer views']));
57   }
58
59   /**
60    * Tests the dependency handling of the moderation state filter.
61    *
62    * @covers ::calculateDependencies
63    * @covers ::onDependencyRemoval
64    */
65   public function testModerationStateFilterDependencyHandling() {
66     // First, check that the view doesn't have any config dependency when there
67     // are no states configured in the filter.
68     $view_id = 'test_content_moderation_state_filter_base_table';
69     $view = Views::getView($view_id);
70
71     $this->assertWorkflowDependencies([], $view);
72     $this->assertTrue($view->storage->status());
73
74     // Configure the Editorial workflow for a node bundle, set the filter value
75     // to use one of its states and check that the workflow is now a dependency
76     // of the view.
77     $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/type/node', [
78       'bundles[example_a]' => TRUE,
79     ], 'Save');
80
81     $edit['options[value][]'] = ['editorial-published'];
82     $this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", $edit, 'Apply');
83     $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
84
85     $view = Views::getView($view_id);
86     $this->assertWorkflowDependencies(['editorial'], $view);
87     $this->assertTrue($view->storage->status());
88
89     // Create another workflow and repeat the checks above.
90     $this->drupalPostForm('admin/config/workflow/workflows/add', [
91       'label' => 'Translation',
92       'id' => 'translation',
93       'workflow_type' => 'content_moderation',
94     ], 'Save');
95     $this->drupalPostForm('admin/config/workflow/workflows/manage/translation/add_state', [
96       'label' => 'Needs Review',
97       'id' => 'needs_review',
98     ], 'Save');
99     $this->drupalPostForm('admin/config/workflow/workflows/manage/translation/type/node', [
100       'bundles[example_b]' => TRUE,
101     ], 'Save');
102
103     $edit['options[value][]'] = ['editorial-published', 'translation-needs_review'];
104     $this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", $edit, 'Apply');
105     $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
106
107     $view = Views::getView($view_id);
108     $this->assertWorkflowDependencies(['editorial', 'translation'], $view);
109     $this->assertTrue(isset($view->storage->getDisplay('default')['display_options']['filters']['moderation_state']));
110     $this->assertTrue($view->storage->status());
111
112     // Remove the 'Translation' workflow.
113     $this->drupalPostForm('admin/config/workflow/workflows/manage/translation/delete', [], 'Delete');
114
115     // Check that the view has been disabled, the filter has been deleted, the
116     // view can be saved and there are no more config dependencies.
117     $view = Views::getView($view_id);
118     $this->assertFalse($view->storage->status());
119     $this->assertFalse(isset($view->storage->getDisplay('default')['display_options']['filters']['moderation_state']));
120     $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
121     $this->assertWorkflowDependencies([], $view);
122   }
123
124   /**
125    * Tests the moderation state filter when the configured workflow is changed.
126    *
127    * @dataProvider providerTestWorkflowChanges
128    */
129   public function testWorkflowChanges($view_id, $filter_name) {
130     // Update the view and make the default filter not exposed anymore,
131     // otherwise all results will be shown when there are no more moderated
132     // bundles left.
133     $this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", [], 'Hide filter');
134     $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
135
136     // First, apply the Editorial workflow to both of our content types.
137     $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/type/node', [
138       'bundles[example_a]' => TRUE,
139       'bundles[example_b]' => TRUE,
140     ], 'Save');
141     \Drupal::service('entity_type.bundle.info')->clearCachedBundles();
142
143     // Add a few nodes in various moderation states.
144     $this->createNode(['type' => 'example_a', 'moderation_state' => 'published']);
145     $this->createNode(['type' => 'example_b', 'moderation_state' => 'published']);
146     $archived_node_a = $this->createNode(['type' => 'example_a', 'moderation_state' => 'archived']);
147     $archived_node_b = $this->createNode(['type' => 'example_b', 'moderation_state' => 'archived']);
148
149     // Configure the view to only show nodes in the 'archived' moderation state.
150     $edit['options[value][]'] = ['editorial-archived'];
151     $this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", $edit, 'Apply');
152     $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
153
154     // Check that only the archived nodes from both bundles are displayed by the
155     // view.
156     $view = Views::getView($view_id);
157     $this->executeView($view);
158     $this->assertIdenticalResultset($view, [['nid' => $archived_node_a->id()], ['nid' => $archived_node_b->id()]], ['nid' => 'nid']);
159
160     // Remove the Editorial workflow from one of the bundles.
161     $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/type/node', [
162       'bundles[example_a]' => TRUE,
163       'bundles[example_b]' => FALSE,
164     ], 'Save');
165     \Drupal::service('entity_type.bundle.info')->clearCachedBundles();
166
167     $view = Views::getView($view_id);
168     $this->executeView($view);
169     $this->assertIdenticalResultset($view, [['nid' => $archived_node_a->id()]], ['nid' => 'nid']);
170
171     // Check that the view can still be edited and saved without any
172     // intervention.
173     $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
174
175     // Remove the Editorial workflow from both bundles.
176     $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/type/node', [
177       'bundles[example_a]' => FALSE,
178       'bundles[example_b]' => FALSE,
179     ], 'Save');
180     \Drupal::service('entity_type.bundle.info')->clearCachedBundles();
181
182     $view = Views::getView($view_id);
183     $this->executeView($view);
184
185     // Check that the view doesn't return any result.
186     $this->assertEmpty($view->result);
187
188     // Check that the view can not be edited without any intervention anymore
189     // because the user needs to fix the filter.
190     $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
191     $this->assertSession()->pageTextContains("No valid values found on filter: $filter_name.");
192   }
193
194   /**
195    * Data provider for testWorkflowChanges.
196    *
197    * @return string[]
198    *   An array of view IDs.
199    */
200   public function providerTestWorkflowChanges() {
201     return [
202       'view on base table, filter on base table' => [
203         'test_content_moderation_state_filter_base_table',
204         'Content: Moderation state'
205       ],
206       'view on base table, filter on revision table' => [
207         'test_content_moderation_state_filter_base_table_filter_on_revision',
208         'Content revision: Moderation state'
209       ],
210     ];
211   }
212
213   /**
214    * Tests the content moderation state filter caching is correct.
215    */
216   public function testFilterRenderCache() {
217     // Initially all states of the workflow are displayed.
218     $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/type/node', [
219       'bundles[example_a]' => TRUE,
220     ], 'Save');
221     $this->assertFilterStates(['All', 'editorial-draft', 'editorial-published', 'editorial-archived']);
222
223     // Adding a new state to the editorial workflow will display that state in
224     // the list of filters.
225     $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/add_state', [
226       'label' => 'Foo',
227       'id' => 'foo',
228     ], 'Save');
229     $this->assertFilterStates(['All', 'editorial-draft', 'editorial-published', 'editorial-archived', 'editorial-foo']);
230
231     // Adding a second workflow to nodes will also show new states.
232     $this->drupalPostForm('admin/config/workflow/workflows/manage/new_workflow/type/node', [
233       'bundles[example_b]' => TRUE,
234     ], 'Save');
235     $this->assertFilterStates(['All', 'editorial-draft', 'editorial-published', 'editorial-archived', 'editorial-foo', 'new_workflow-draft', 'new_workflow-published', 'new_workflow-bar']);
236
237     // Add a few more states and change the exposed filter to allow multiple
238     // selections so we can check that the size of the select element does not
239     // exceed 8 options.
240     $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/add_state', [
241       'label' => 'Foo 2',
242       'id' => 'foo2',
243     ], 'Save');
244     $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/add_state', [
245       'label' => 'Foo 3',
246       'id' => 'foo3',
247     ], 'Save');
248
249     $view_id = 'test_content_moderation_state_filter_base_table';
250     $edit['options[expose][multiple]'] = TRUE;
251     $this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/filter/moderation_state", $edit, 'Apply');
252     $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
253
254     $this->assertFilterStates(['editorial-draft', 'editorial-published', 'editorial-archived', 'editorial-foo', 'editorial-foo2', 'editorial-foo3', 'new_workflow-draft', 'new_workflow-published', 'new_workflow-bar'], TRUE);
255   }
256
257   /**
258    * Assert the states which appear in the filter.
259    *
260    * @param array $states
261    *   The states which should appear in the filter.
262    * @param bool $check_size
263    *   (optional) Whether to check that size of the select element is not
264    *   greater than 8. Defaults to FALSE.
265    */
266   protected function assertFilterStates($states, $check_size = FALSE) {
267     $this->drupalGet('/filter-test-path');
268
269     $assert_session = $this->assertSession();
270
271     // Check that the select contains the correct number of options.
272     $assert_session->elementsCount('css', '#edit-default-revision-state option', count($states));
273
274     // Check that the size of the select element does not exceed 8 options.
275     if ($check_size) {
276       $this->assertGreaterThan(8, count($states));
277       $assert_session->elementAttributeContains('css', '#edit-default-revision-state', 'size', 8);
278     }
279
280     // Check that an option exists for each of the expected states.
281     foreach ($states as $state) {
282       $assert_session->optionExists('Default Revision State', $state);
283     }
284   }
285
286   /**
287    * Asserts the views dependencies on workflow config entities.
288    *
289    * @param string[] $workflow_ids
290    *   An array of workflow IDs to check.
291    * @param \Drupal\views\ViewExecutable $view
292    *   An executable View object.
293    */
294   protected function assertWorkflowDependencies(array $workflow_ids, ViewExecutable $view) {
295     $dependencies = $view->getDependencies();
296
297     $expected = [];
298     foreach (Workflow::loadMultiple($workflow_ids) as $workflow) {
299       $expected[] = $workflow->getConfigDependencyName();
300     }
301
302     if ($expected) {
303       $this->assertSame($expected, $dependencies['config']);
304     }
305     else {
306       $this->assertTrue(!isset($dependencies['config']));
307     }
308   }
309
310 }