Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / workflows / tests / src / Unit / WorkflowTest.php
1 <?php
2
3 namespace Drupal\Tests\workflows\Unit;
4
5 use Drupal\Core\DependencyInjection\ContainerBuilder;
6 use Drupal\Tests\UnitTestCase;
7 use Drupal\workflow_type_test\Plugin\WorkflowType\TestType;
8 use Drupal\workflows\Entity\Workflow;
9 use Drupal\workflows\State;
10 use Drupal\workflows\Transition;
11 use Drupal\workflows\WorkflowTypeManager;
12 use Prophecy\Argument;
13
14 /**
15  * @coversDefaultClass \Drupal\workflows\Plugin\WorkflowTypeBase
16  *
17  * @group workflows
18  */
19 class WorkflowTest extends UnitTestCase {
20
21   /**
22    * {@inheritdoc}
23    */
24   protected function setUp() {
25     parent::setUp();
26     // Create a container so that the plugin manager and workflow type can be
27     // mocked.
28     $container = new ContainerBuilder();
29     $workflow_manager = $this->prophesize(WorkflowTypeManager::class);
30     $workflow_manager->createInstance('test_type', Argument::any())->willReturn(new TestType([], '', []));
31     $container->set('plugin.manager.workflows.type', $workflow_manager->reveal());
32     \Drupal::setContainer($container);
33   }
34
35   /**
36    * @covers ::addState
37    * @covers ::hasState
38    */
39   public function testAddAndHasState() {
40     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
41     $this->assertFalse($workflow->getTypePlugin()->hasState('draft'));
42
43     // By default states are ordered in the order added.
44     $workflow->getTypePlugin()->addState('draft', 'Draft');
45     $this->assertTrue($workflow->getTypePlugin()->hasState('draft'));
46     $this->assertFalse($workflow->getTypePlugin()->hasState('published'));
47     $this->assertEquals(0, $workflow->getTypePlugin()->getState('draft')->weight());
48     // Adding a state does not set up a transition to itself.
49     $this->assertFalse($workflow->getTypePlugin()->hasTransitionFromStateToState('draft', 'draft'));
50
51     // New states are added with a new weight 1 more than the current highest
52     // weight.
53     $workflow->getTypePlugin()->addState('published', 'Published');
54     $this->assertEquals(1, $workflow->getTypePlugin()->getState('published')->weight());
55   }
56
57   /**
58    * @covers ::addState
59    */
60   public function testAddStateException() {
61     $this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' already exists in workflow.");
62     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
63     $workflow->getTypePlugin()->addState('draft', 'Draft');
64     $workflow->getTypePlugin()->addState('draft', 'Draft');
65   }
66
67   /**
68    * @covers ::addState
69    */
70   public function testAddStateInvalidIdException() {
71     $this->setExpectedException(\InvalidArgumentException::class, "The state ID 'draft-draft' must contain only lowercase letters, numbers, and underscores");
72     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
73     $workflow->getTypePlugin()->addState('draft-draft', 'Draft');
74   }
75
76   /**
77    * @covers ::getStates
78    */
79   public function testGetStates() {
80     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
81
82     // Getting states works when there are none.
83     $this->assertArrayEquals([], array_keys($workflow->getTypePlugin()->getStates()));
84     $this->assertArrayEquals([], array_keys($workflow->getTypePlugin()->getStates([])));
85
86     $workflow
87       ->getTypePlugin()
88       ->addState('draft', 'Draft')
89       ->addState('published', 'Published')
90       ->addState('archived', 'Archived');
91
92     // States are stored in alphabetical key order.
93     $this->assertArrayEquals([
94       'archived',
95       'draft',
96       'published',
97     ], array_keys($workflow->getTypePlugin()->getConfiguration()['states']));
98
99     // Ensure we're returning state objects.
100     $this->assertInstanceOf(State::class, $workflow->getTypePlugin()->getStates()['draft']);
101
102     // Passing in no IDs returns all states.
103     $this->assertArrayEquals(['draft', 'published', 'archived'], array_keys($workflow->getTypePlugin()->getStates()));
104
105     // The order of states is by weight.
106     $workflow->getTypePlugin()->setStateWeight('published', -1);
107     $this->assertArrayEquals(['published', 'draft', 'archived'], array_keys($workflow->getTypePlugin()->getStates()));
108
109     // The label is also used for sorting if weights are equal.
110     $workflow->getTypePlugin()->setStateWeight('archived', 0);
111     $this->assertArrayEquals(['published', 'archived', 'draft'], array_keys($workflow->getTypePlugin()->getStates()));
112
113     // You can limit the states returned by passing in states IDs.
114     $this->assertArrayEquals(['archived', 'draft'], array_keys($workflow->getTypePlugin()->getStates(['draft', 'archived'])));
115
116     // An empty array does not load all states.
117     $this->assertArrayEquals([], array_keys($workflow->getTypePlugin()->getStates([])));
118   }
119
120   /**
121    * Test numeric IDs when added to a workflow.
122    */
123   public function testNumericIdSorting() {
124     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
125     $workflow_type = $workflow->getTypePlugin();
126
127     $workflow_type->addState('1', 'One');
128     $workflow_type->addState('2', 'Two');
129     $workflow_type->addState('3', 'ZZZ');
130     $workflow_type->addState('4', 'AAA');
131
132     $workflow_type->setStateWeight('1', 1);
133     $workflow_type->setStateWeight('2', 2);
134     $workflow_type->setStateWeight('3', 3);
135     $workflow_type->setStateWeight('4', 3);
136
137     // Ensure numeric states are correctly sorted by weight first, label second.
138     $this->assertEquals([1, 2, 4, 3], array_keys($workflow_type->getStates()));
139   }
140
141   /**
142    * @covers ::getStates
143    */
144   public function testGetStatesException() {
145     $this->setExpectedException(\InvalidArgumentException::class, "The state 'state_that_does_not_exist' does not exist in workflow.");
146     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
147     $workflow->getTypePlugin()->getStates(['state_that_does_not_exist']);
148   }
149
150   /**
151    * @covers ::getState
152    */
153   public function testGetState() {
154     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
155     // By default states are ordered in the order added.
156     $workflow
157       ->getTypePlugin()
158       ->addState('draft', 'Draft')
159       ->addState('published', 'Published')
160       ->addState('archived', 'Archived')
161       ->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft')
162       ->addTransition('publish', 'Publish', ['draft'], 'published');
163
164     // Ensure we're returning state objects and they are set up correctly
165     $this->assertInstanceOf(State::class, $workflow->getTypePlugin()->getState('draft'));
166     $this->assertEquals('archived', $workflow->getTypePlugin()->getState('archived')->id());
167     $this->assertEquals('Archived', $workflow->getTypePlugin()->getState('archived')->label());
168
169     $draft = $workflow->getTypePlugin()->getState('draft');
170     $this->assertTrue($draft->canTransitionTo('draft'));
171     $this->assertTrue($draft->canTransitionTo('published'));
172     $this->assertFalse($draft->canTransitionTo('archived'));
173     $this->assertEquals('Publish', $draft->getTransitionTo('published')->label());
174     $this->assertEquals(0, $draft->weight());
175     $this->assertEquals(1, $workflow->getTypePlugin()->getState('published')->weight());
176     $this->assertEquals(2, $workflow->getTypePlugin()->getState('archived')->weight());
177   }
178
179   /**
180    * @covers ::getState
181    */
182   public function testGetStateException() {
183     $this->setExpectedException(\InvalidArgumentException::class, "The state 'state_that_does_not_exist' does not exist in workflow.");
184     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
185     $workflow->getTypePlugin()->getState('state_that_does_not_exist');
186   }
187
188   /**
189    * @covers ::setStateLabel
190    */
191   public function testSetStateLabel() {
192     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
193     $workflow->getTypePlugin()->addState('draft', 'Draft');
194     $this->assertEquals('Draft', $workflow->getTypePlugin()->getState('draft')->label());
195     $workflow->getTypePlugin()->setStateLabel('draft', 'Unpublished');
196     $this->assertEquals('Unpublished', $workflow->getTypePlugin()->getState('draft')->label());
197   }
198
199   /**
200    * @covers ::setStateLabel
201    */
202   public function testSetStateLabelException() {
203     $this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' does not exist in workflow.");
204     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
205     $workflow->getTypePlugin()->setStateLabel('draft', 'Draft');
206   }
207
208   /**
209    * @covers ::setStateWeight
210    */
211   public function testSetStateWeight() {
212     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
213     $workflow->getTypePlugin()->addState('draft', 'Draft');
214     $this->assertEquals(0, $workflow->getTypePlugin()->getState('draft')->weight());
215     $workflow->getTypePlugin()->setStateWeight('draft', -10);
216     $this->assertEquals(-10, $workflow->getTypePlugin()->getState('draft')->weight());
217   }
218
219   /**
220    * @covers ::setStateWeight
221    */
222   public function testSetStateWeightException() {
223     $this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' does not exist in workflow.");
224     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
225     $workflow->getTypePlugin()->setStateWeight('draft', 10);
226   }
227
228   /**
229    * @covers ::setStateWeight
230    */
231   public function testSetStateWeightNonNumericException() {
232     $this->setExpectedException(\InvalidArgumentException::class, "The weight 'foo' must be numeric for state 'Published'.");
233     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
234     $workflow->getTypePlugin()->addState('published', 'Published');
235     $workflow->getTypePlugin()->setStateWeight('published', 'foo');
236   }
237
238   /**
239    * @covers ::deleteState
240    */
241   public function testDeleteState() {
242     $workflow_type = new TestType([], '', []);
243     $workflow_type
244       ->addState('draft', 'Draft')
245       ->addState('published', 'Published')
246       ->addState('archived', 'Archived')
247       ->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
248       ->addTransition('create_new_draft', 'Create new draft', ['draft', 'published'], 'draft')
249       ->addTransition('archive', 'Archive', ['draft', 'published'], 'archived');
250     $this->assertCount(3, $workflow_type->getStates());
251     $this->assertCount(3, $workflow_type->getState('published')->getTransitions());
252     $workflow_type->deleteState('draft');
253     $this->assertFalse($workflow_type->hasState('draft'));
254     $this->assertCount(2, $workflow_type->getStates());
255     $this->assertCount(2, $workflow_type->getState('published')->getTransitions());
256     $workflow_type->deleteState('published');
257     $this->assertCount(0, $workflow_type->getTransitions());
258   }
259
260   /**
261    * @covers ::deleteState
262    */
263   public function testDeleteStateException() {
264     $this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' does not exist in workflow.");
265     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
266     $workflow->getTypePlugin()->deleteState('draft');
267   }
268
269   /**
270    * @covers ::deleteState
271    */
272   public function testDeleteOnlyStateException() {
273     $this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' can not be deleted from workflow as it is the only state");
274     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
275     $workflow->getTypePlugin()->addState('draft', 'Draft');
276     $workflow->getTypePlugin()->deleteState('draft');
277   }
278
279   /**
280    * @covers ::addTransition
281    * @covers ::hasTransition
282    */
283   public function testAddTransition() {
284     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
285
286     // By default states are ordered in the order added.
287     $workflow
288       ->getTypePlugin()
289       ->addState('draft', 'Draft')
290       ->addState('published', 'Published');
291
292     $this->assertFalse($workflow->getTypePlugin()->getState('draft')->canTransitionTo('published'));
293     $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['draft'], 'published');
294     $this->assertTrue($workflow->getTypePlugin()->getState('draft')->canTransitionTo('published'));
295     $this->assertEquals(0, $workflow->getTypePlugin()->getTransition('publish')->weight());
296     $this->assertTrue($workflow->getTypePlugin()->hasTransition('publish'));
297     $this->assertFalse($workflow->getTypePlugin()->hasTransition('draft'));
298
299     $workflow->getTypePlugin()->addTransition('save_publish', 'Save', ['published'], 'published');
300     $this->assertEquals(1, $workflow->getTypePlugin()->getTransition('save_publish')->weight());
301   }
302
303   /**
304    * @covers ::addTransition
305    */
306   public function testAddTransitionDuplicateException() {
307     $this->setExpectedException(\InvalidArgumentException::class, "The transition 'publish' already exists in workflow.");
308     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
309     $workflow->getTypePlugin()->addState('published', 'Published');
310     $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['published'], 'published');
311     $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['published'], 'published');
312   }
313
314   /**
315    * @covers ::addTransition
316    */
317   public function testAddTransitionInvalidIdException() {
318     $this->setExpectedException(\InvalidArgumentException::class, "The transition ID 'publish-publish' must contain only lowercase letters, numbers, and underscores");
319     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
320     $workflow->getTypePlugin()->addState('published', 'Published');
321     $workflow->getTypePlugin()->addTransition('publish-publish', 'Publish', ['published'], 'published');
322   }
323
324   /**
325    * @covers ::addTransition
326    */
327   public function testAddTransitionMissingFromException() {
328     $this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' does not exist in workflow.");
329     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
330     $workflow->getTypePlugin()->addState('published', 'Published');
331     $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['draft'], 'published');
332   }
333
334   /**
335    * @covers ::addTransition
336    */
337   public function testAddTransitionDuplicateTransitionStatesException() {
338     $this->setExpectedException(\InvalidArgumentException::class, "The 'publish' transition already allows 'draft' to 'published' transitions in workflow.");
339     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
340     $workflow
341       ->getTypePlugin()
342       ->addState('draft', 'Draft')
343       ->addState('published', 'Published');
344     $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['draft', 'published'], 'published');
345     $workflow->getTypePlugin()->addTransition('draft_to_published', 'Publish a draft', ['draft'], 'published');
346   }
347
348   /**
349    * @covers ::addTransition
350    */
351   public function testAddTransitionConsistentAfterFromCatch() {
352     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
353     $workflow->getTypePlugin()->addState('published', 'Published');
354     try {
355       $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['draft'], 'published');
356     }
357     catch (\InvalidArgumentException $e) {
358     }
359     // Ensure that the workflow is not left in an inconsistent state after an
360     // exception is thrown from Workflow::setTransitionFromStates() whilst
361     // calling Workflow::addTransition().
362     $this->assertFalse($workflow->getTypePlugin()->hasTransition('publish'));
363   }
364
365   /**
366    * @covers ::addTransition
367    */
368   public function testAddTransitionMissingToException() {
369     $this->setExpectedException(\InvalidArgumentException::class, "The state 'published' does not exist in workflow.");
370     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
371     $workflow->getTypePlugin()->addState('draft', 'Draft');
372     $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['draft'], 'published');
373   }
374
375   /**
376    * @covers ::getTransitions
377    * @covers ::setTransitionWeight
378    */
379   public function testGetTransitions() {
380     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
381
382     // Getting transitions works when there are none.
383     $this->assertArrayEquals([], array_keys($workflow->getTypePlugin()->getTransitions()));
384     $this->assertArrayEquals([], array_keys($workflow->getTypePlugin()->getTransitions([])));
385
386     // By default states are ordered in the order added.
387     $workflow
388       ->getTypePlugin()
389       ->addState('a', 'A')
390       ->addState('b', 'B')
391       ->addTransition('a_b', 'A to B', ['a'], 'b')
392       ->addTransition('a_a', 'A to A', ['a'], 'a');
393
394     // Transitions are stored in alphabetical key order in configuration.
395     $this->assertArrayEquals(['a_a', 'a_b'], array_keys($workflow->getTypePlugin()->getConfiguration()['transitions']));
396
397     // Ensure we're returning transition objects.
398     $this->assertInstanceOf(Transition::class, $workflow->getTypePlugin()->getTransitions()['a_a']);
399
400     // Passing in no IDs returns all transitions.
401     $this->assertArrayEquals(['a_b', 'a_a'], array_keys($workflow->getTypePlugin()->getTransitions()));
402
403     // The order of states is by weight.
404     $workflow->getTypePlugin()->setTransitionWeight('a_a', -1);
405     $this->assertArrayEquals(['a_a', 'a_b'], array_keys($workflow->getTypePlugin()->getTransitions()));
406
407     // If all weights are equal it will fallback to labels.
408     $workflow->getTypePlugin()->setTransitionWeight('a_a', 0);
409     $this->assertArrayEquals(['a_a', 'a_b'], array_keys($workflow->getTypePlugin()->getTransitions()));
410     $workflow->getTypePlugin()->setTransitionLabel('a_b', 'A B');
411     $this->assertArrayEquals(['a_b', 'a_a'], array_keys($workflow->getTypePlugin()->getTransitions()));
412
413     // You can limit the states returned by passing in states IDs.
414     $this->assertArrayEquals(['a_a'], array_keys($workflow->getTypePlugin()->getTransitions(['a_a'])));
415
416     // An empty array does not load all states.
417     $this->assertArrayEquals([], array_keys($workflow->getTypePlugin()->getTransitions([])));
418   }
419
420   /**
421    * @covers ::getTransition
422    */
423   public function testGetTransition() {
424     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
425     // By default states are ordered in the order added.
426     $workflow
427       ->getTypePlugin()
428       ->addState('draft', 'Draft')
429       ->addState('published', 'Published')
430       ->addState('archived', 'Archived')
431       ->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft')
432       ->addTransition('publish', 'Publish', ['draft'], 'published');
433
434     // Ensure we're returning state objects and they are set up correctly
435     $this->assertInstanceOf(Transition::class, $workflow->getTypePlugin()->getTransition('create_new_draft'));
436     $this->assertEquals('publish', $workflow->getTypePlugin()->getTransition('publish')->id());
437     $this->assertEquals('Publish', $workflow->getTypePlugin()->getTransition('publish')->label());
438
439     $transition = $workflow->getTypePlugin()->getTransition('publish');
440     $this->assertEquals($workflow->getTypePlugin()->getState('draft'), $transition->from()['draft']);
441     $this->assertEquals($workflow->getTypePlugin()->getState('published'), $transition->to());
442   }
443
444   /**
445    * @covers ::getTransition
446    */
447   public function testGetTransitionException() {
448     $this->setExpectedException(\InvalidArgumentException::class, "The transition 'transition_that_does_not_exist' does not exist in workflow.");
449     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
450     $workflow->getTypePlugin()->getTransition('transition_that_does_not_exist');
451   }
452
453   /**
454    * @covers ::getTransitionsForState
455    */
456   public function testGetTransitionsForState() {
457     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
458     // By default states are ordered in the order added.
459     $workflow
460       ->getTypePlugin()
461       ->addState('draft', 'Draft')
462       ->addState('published', 'Published')
463       ->addState('archived', 'Archived')
464       ->addTransition('create_new_draft', 'Create new draft', ['archived', 'draft'], 'draft')
465       ->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
466       ->addTransition('archive', 'Archive', ['published'], 'archived');
467
468     $this->assertEquals(['create_new_draft', 'publish'], array_keys($workflow->getTypePlugin()->getTransitionsForState('draft')));
469     $this->assertEquals(['create_new_draft'], array_keys($workflow->getTypePlugin()->getTransitionsForState('draft', 'to')));
470     $this->assertEquals(['publish', 'archive'], array_keys($workflow->getTypePlugin()->getTransitionsForState('published')));
471     $this->assertEquals(['publish'], array_keys($workflow->getTypePlugin()->getTransitionsForState('published', 'to')));
472     $this->assertEquals(['create_new_draft'], array_keys($workflow->getTypePlugin()->getTransitionsForState('archived', 'from')));
473     $this->assertEquals(['archive'], array_keys($workflow->getTypePlugin()->getTransitionsForState('archived', 'to')));
474   }
475
476   /**
477    * @covers ::getTransitionFromStateToState
478    * @covers ::hasTransitionFromStateToState
479    */
480   public function testGetTransitionFromStateToState() {
481     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
482     // By default states are ordered in the order added.
483     $workflow
484       ->getTypePlugin()
485       ->addState('draft', 'Draft')
486       ->addState('published', 'Published')
487       ->addState('archived', 'Archived')
488       ->addTransition('create_new_draft', 'Create new draft', ['archived', 'draft'], 'draft')
489       ->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
490       ->addTransition('archive', 'Archive', ['published'], 'archived');
491
492     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('draft', 'published'));
493     $this->assertFalse($workflow->getTypePlugin()->hasTransitionFromStateToState('archived', 'archived'));
494     $transition = $workflow->getTypePlugin()->getTransitionFromStateToState('published', 'archived');
495     $this->assertEquals('Archive', $transition->label());
496   }
497
498   /**
499    * @covers ::getTransitionFromStateToState
500    */
501   public function testGetTransitionFromStateToStateException() {
502     $this->setExpectedException(\InvalidArgumentException::class, "The transition from 'archived' to 'archived' does not exist in workflow.");
503     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
504     // By default states are ordered in the order added.
505     $workflow
506       ->getTypePlugin()
507       ->addState('draft', 'Draft')
508       ->addState('published', 'Published')
509       ->addState('archived', 'Archived')
510       ->addTransition('create_new_draft', 'Create new draft', ['archived', 'draft'], 'draft')
511       ->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
512       ->addTransition('archive', 'Archive', ['published'], 'archived');
513
514     $workflow->getTypePlugin()->getTransitionFromStateToState('archived', 'archived');
515   }
516
517   /**
518    * @covers ::setTransitionLabel
519    */
520   public function testSetTransitionLabel() {
521     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
522     $workflow
523       ->getTypePlugin()
524       ->addState('draft', 'Draft')
525       ->addState('published', 'Published')
526       ->addTransition('publish', 'Publish', ['draft'], 'published');
527     $this->assertEquals('Publish', $workflow->getTypePlugin()->getTransition('publish')->label());
528     $workflow->getTypePlugin()->setTransitionLabel('publish', 'Publish!');
529     $this->assertEquals('Publish!', $workflow->getTypePlugin()->getTransition('publish')->label());
530   }
531
532   /**
533    * @covers ::setTransitionLabel
534    */
535   public function testSetTransitionLabelException() {
536     $this->setExpectedException(\InvalidArgumentException::class, "The transition 'draft-published' does not exist in workflow.");
537     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
538     $workflow->getTypePlugin()->addState('published', 'Published');
539     $workflow->getTypePlugin()->setTransitionLabel('draft-published', 'Publish');
540   }
541
542   /**
543    * @covers ::setTransitionWeight
544    */
545   public function testSetTransitionWeight() {
546     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
547     $workflow
548       ->getTypePlugin()
549       ->addState('draft', 'Draft')
550       ->addState('published', 'Published')
551       ->addTransition('publish', 'Publish', ['draft'], 'published');
552     $this->assertEquals(0, $workflow->getTypePlugin()->getTransition('publish')->weight());
553     $workflow->getTypePlugin()->setTransitionWeight('publish', 10);
554     $this->assertEquals(10, $workflow->getTypePlugin()->getTransition('publish')->weight());
555   }
556
557   /**
558    * @covers ::setTransitionWeight
559    */
560   public function testSetTransitionWeightException() {
561     $this->setExpectedException(\InvalidArgumentException::class, "The transition 'draft-published' does not exist in workflow.");
562     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
563     $workflow->getTypePlugin()->addState('published', 'Published');
564     $workflow->getTypePlugin()->setTransitionWeight('draft-published', 10);
565   }
566
567   /**
568    * @covers ::setTransitionWeight
569    */
570   public function testSetTransitionWeightNonNumericException() {
571     $this->setExpectedException(\InvalidArgumentException::class, "The weight 'foo' must be numeric for transition 'Publish'.");
572     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
573     $workflow->getTypePlugin()->addState('published', 'Published');
574     $workflow->getTypePlugin()->addTransition('publish', 'Publish', [], 'published');
575     $workflow->getTypePlugin()->setTransitionWeight('publish', 'foo');
576   }
577
578   /**
579    * @covers ::setTransitionFromStates
580    */
581   public function testSetTransitionFromStates() {
582     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
583     $workflow
584       ->getTypePlugin()
585       ->addState('draft', 'Draft')
586       ->addState('published', 'Published')
587       ->addState('archived', 'Archived')
588       ->addTransition('test', 'Test', ['draft'], 'draft');
589
590     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('draft', 'draft'));
591     $this->assertFalse($workflow->getTypePlugin()->hasTransitionFromStateToState('published', 'draft'));
592     $this->assertFalse($workflow->getTypePlugin()->hasTransitionFromStateToState('archived', 'draft'));
593     $workflow->getTypePlugin()->setTransitionFromStates('test', ['draft', 'published', 'archived']);
594     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('draft', 'draft'));
595     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('published', 'draft'));
596     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('archived', 'draft'));
597     $workflow->getTypePlugin()->setTransitionFromStates('test', ['published', 'archived']);
598     $this->assertFalse($workflow->getTypePlugin()->hasTransitionFromStateToState('draft', 'draft'));
599     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('published', 'draft'));
600     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('archived', 'draft'));
601   }
602
603   /**
604    * @covers ::setTransitionFromStates
605    */
606   public function testSetTransitionFromStatesMissingTransition() {
607     $this->setExpectedException(\InvalidArgumentException::class, "The transition 'test' does not exist in workflow.");
608     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
609     $workflow
610       ->getTypePlugin()
611       ->addState('draft', 'Draft')
612       ->addState('published', 'Published')
613       ->addState('archived', 'Archived')
614       ->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft');
615
616     $workflow->getTypePlugin()->setTransitionFromStates('test', ['draft', 'published', 'archived']);
617   }
618
619   /**
620    * @covers ::setTransitionFromStates
621    */
622   public function testSetTransitionFromStatesMissingState() {
623     $this->setExpectedException(\InvalidArgumentException::class, "The state 'published' does not exist in workflow.");
624     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
625     $workflow
626       ->getTypePlugin()
627       ->addState('draft', 'Draft')
628       ->addState('archived', 'Archived')
629       ->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft');
630
631     $workflow->getTypePlugin()->setTransitionFromStates('create_new_draft', ['draft', 'published', 'archived']);
632   }
633
634   /**
635    * @covers ::setTransitionFromStates
636    */
637   public function testSetTransitionFromStatesAlreadyExists() {
638     $this->setExpectedException(\InvalidArgumentException::class, "The 'create_new_draft' transition already allows 'draft' to 'draft' transitions in workflow.");
639     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
640     $workflow
641       ->getTypePlugin()
642       ->addState('draft', 'Draft')
643       ->addState('archived', 'Archived')
644       ->addState('needs_review', 'Needs Review')
645       ->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft')
646       ->addTransition('needs_review', 'Needs review', ['needs_review'], 'draft');
647
648     $workflow->getTypePlugin()->setTransitionFromStates('needs_review', ['draft']);
649   }
650
651   /**
652    * @covers ::deleteTransition
653    */
654   public function testDeleteTransition() {
655     $workflow_type = new TestType([], '', []);
656     $workflow_type
657       ->addState('draft', 'Draft')
658       ->addState('published', 'Published')
659       ->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft')
660       ->addTransition('publish', 'Publish', ['draft'], 'published');
661     $this->assertTrue($workflow_type->getState('draft')->canTransitionTo('published'));
662     $workflow_type->deleteTransition('publish');
663     $this->assertFalse($workflow_type->getState('draft')->canTransitionTo('published'));
664     $this->assertTrue($workflow_type->getState('draft')->canTransitionTo('draft'));
665   }
666
667   /**
668    * @covers ::deleteTransition
669    */
670   public function testDeleteTransitionException() {
671     $this->setExpectedException(\InvalidArgumentException::class, "The transition 'draft-published' does not exist in workflow.");
672     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
673     $workflow->getTypePlugin()->addState('published', 'Published');
674     $workflow->getTypePlugin()->deleteTransition('draft-published');
675   }
676
677   /**
678    * @covers \Drupal\workflows\Entity\Workflow::status
679    */
680   public function testStatus() {
681     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
682     $this->assertFalse($workflow->status());
683     $workflow->getTypePlugin()->addState('published', 'Published');
684     $this->assertTrue($workflow->status());
685   }
686
687 }