Updated Drupal to 8.6. This goes with the following updates because it's possible...
[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\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;
11
12 /**
13  * Tests the views 'moderation_state_filter' filter plugin.
14  *
15  * @coversDefaultClass \Drupal\content_moderation\Plugin\views\filter\ModerationStateFilter
16  *
17  * @group content_moderation
18  */
19 class ViewsModerationStateFilterTest extends ViewTestBase {
20
21   use ContentModerationTestTrait;
22
23   /**
24    * {@inheritdoc}
25    */
26   public static $modules = [
27     'content_moderation_test_views',
28     'node',
29     'content_moderation',
30     'workflows',
31     'workflow_type_test',
32     'entity_test',
33     'language',
34     'content_translation',
35     'views_ui',
36   ];
37
38   /**
39    * {@inheritdoc}
40    */
41   protected function setUp($import_test_views = TRUE) {
42     parent::setUp(FALSE);
43
44     NodeType::create([
45       'type' => 'example_a',
46     ])->save();
47     NodeType::create([
48       'type' => 'example_b',
49     ])->save();
50
51     $this->createEditorialWorkflow();
52
53     $new_workflow = Workflow::create([
54       'type' => 'content_moderation',
55       'id' => 'new_workflow',
56       'label' => 'New workflow',
57     ]);
58     $new_workflow->getTypePlugin()->addState('bar', 'Bar');
59     $new_workflow->save();
60
61     $this->drupalLogin($this->drupalCreateUser(['administer workflows', 'administer views']));
62   }
63
64   /**
65    * Tests the dependency handling of the moderation state filter.
66    *
67    * @covers ::calculateDependencies
68    * @covers ::onDependencyRemoval
69    */
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);
75
76     $this->assertWorkflowDependencies([], $view);
77     $this->assertTrue($view->storage->status());
78
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
81     // of the view.
82     $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/type/node', [
83       'bundles[example_a]' => TRUE,
84     ], 'Save');
85
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');
89
90     $view = Views::getView($view_id);
91     $this->assertWorkflowDependencies(['editorial'], $view);
92     $this->assertTrue($view->storage->status());
93
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',
99     ], 'Save');
100     $this->drupalPostForm('admin/config/workflow/workflows/manage/translation/add_state', [
101       'label' => 'Needs Review',
102       'id' => 'needs_review',
103     ], 'Save');
104     $this->drupalPostForm('admin/config/workflow/workflows/manage/translation/type/node', [
105       'bundles[example_b]' => TRUE,
106     ], 'Save');
107
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');
111
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());
116
117     // Remove the 'Translation' workflow.
118     $this->drupalPostForm('admin/config/workflow/workflows/manage/translation/delete', [], 'Delete');
119
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);
127   }
128
129   /**
130    * Tests the moderation state filter when the configured workflow is changed.
131    *
132    * @dataProvider providerTestWorkflowChanges
133    */
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
137     // bundles left.
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');
140
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,
145     ], 'Save');
146     \Drupal::service('entity_type.bundle.info')->clearCachedBundles();
147
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']);
153
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');
158
159     // Check that only the archived nodes from both bundles are displayed by the
160     // view.
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']);
164
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,
169     ], 'Save');
170     \Drupal::service('entity_type.bundle.info')->clearCachedBundles();
171
172     $view = Views::getView($view_id);
173     $this->executeView($view);
174     $this->assertIdenticalResultset($view, [['nid' => $archived_node_a->id()]], ['nid' => 'nid']);
175
176     // Check that the view can still be edited and saved without any
177     // intervention.
178     $this->drupalPostForm("admin/structure/views/view/$view_id", [], 'Save');
179
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,
184     ], 'Save');
185     \Drupal::service('entity_type.bundle.info')->clearCachedBundles();
186
187     $view = Views::getView($view_id);
188     $this->executeView($view);
189
190     // Check that the view doesn't return any result.
191     $this->assertEmpty($view->result);
192
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.");
197   }
198
199   /**
200    * Data provider for testWorkflowChanges.
201    *
202    * @return string[]
203    *   An array of view IDs.
204    */
205   public function providerTestWorkflowChanges() {
206     return [
207       'view on base table, filter on base table' => [
208         'test_content_moderation_state_filter_base_table',
209         'Content: Moderation state',
210       ],
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',
214       ],
215     ];
216   }
217
218   /**
219    * Tests the content moderation state filter caching is correct.
220    */
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,
225     ], 'Save');
226     $this->assertFilterStates(['All', 'editorial-draft', 'editorial-published', 'editorial-archived']);
227
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', [
231       'label' => 'Foo',
232       'id' => 'foo',
233     ], 'Save');
234     $this->assertFilterStates(['All', 'editorial-draft', 'editorial-published', 'editorial-archived', 'editorial-foo']);
235
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,
239     ], 'Save');
240     $this->assertFilterStates(['All', 'editorial-draft', 'editorial-published', 'editorial-archived', 'editorial-foo', 'new_workflow-draft', 'new_workflow-published', 'new_workflow-bar']);
241
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
244     // exceed 8 options.
245     $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/add_state', [
246       'label' => 'Foo 2',
247       'id' => 'foo2',
248     ], 'Save');
249     $this->drupalPostForm('admin/config/workflow/workflows/manage/editorial/add_state', [
250       'label' => 'Foo 3',
251       'id' => 'foo3',
252     ], 'Save');
253
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');
258
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);
260   }
261
262   /**
263    * Assert the states which appear in the filter.
264    *
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.
270    */
271   protected function assertFilterStates($states, $check_size = FALSE) {
272     $this->drupalGet('/filter-test-path');
273
274     $assert_session = $this->assertSession();
275
276     // Check that the select contains the correct number of options.
277     $assert_session->elementsCount('css', '#edit-default-revision-state option', count($states));
278
279     // Check that the size of the select element does not exceed 8 options.
280     if ($check_size) {
281       $this->assertGreaterThan(8, count($states));
282       $assert_session->elementAttributeContains('css', '#edit-default-revision-state', 'size', 8);
283     }
284
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);
288     }
289   }
290
291   /**
292    * Asserts the views dependencies on workflow config entities.
293    *
294    * @param string[] $workflow_ids
295    *   An array of workflow IDs to check.
296    * @param \Drupal\views\ViewExecutable $view
297    *   An executable View object.
298    */
299   protected function assertWorkflowDependencies(array $workflow_ids, ViewExecutable $view) {
300     $dependencies = $view->getDependencies();
301
302     $expected = [];
303     foreach (Workflow::loadMultiple($workflow_ids) as $workflow) {
304       $expected[] = $workflow->getConfigDependencyName();
305     }
306
307     if ($expected) {
308       $this->assertSame($expected, $dependencies['config']);
309     }
310     else {
311       $this->assertTrue(!isset($dependencies['config']));
312     }
313   }
314
315 }