Security update for Core, with self-updated composer
[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 ::deleteState
230    */
231   public function testDeleteState() {
232     $workflow_type = new TestType([], '', []);
233     $workflow_type
234       ->addState('draft', 'Draft')
235       ->addState('published', 'Published')
236       ->addState('archived', 'Archived')
237       ->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
238       ->addTransition('create_new_draft', 'Create new draft', ['draft', 'published'], 'draft')
239       ->addTransition('archive', 'Archive', ['draft', 'published'], 'archived');
240     $this->assertCount(3, $workflow_type->getStates());
241     $this->assertCount(3, $workflow_type->getState('published')->getTransitions());
242     $workflow_type->deleteState('draft');
243     $this->assertFalse($workflow_type->hasState('draft'));
244     $this->assertCount(2, $workflow_type->getStates());
245     $this->assertCount(2, $workflow_type->getState('published')->getTransitions());
246     $workflow_type->deleteState('published');
247     $this->assertCount(0, $workflow_type->getTransitions());
248   }
249
250   /**
251    * @covers ::deleteState
252    */
253   public function testDeleteStateException() {
254     $this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' does not exist in workflow.");
255     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
256     $workflow->getTypePlugin()->deleteState('draft');
257   }
258
259   /**
260    * @covers ::deleteState
261    */
262   public function testDeleteOnlyStateException() {
263     $this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' can not be deleted from workflow as it is the only state");
264     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
265     $workflow->getTypePlugin()->addState('draft', 'Draft');
266     $workflow->getTypePlugin()->deleteState('draft');
267   }
268
269   /**
270    * @covers ::addTransition
271    * @covers ::hasTransition
272    */
273   public function testAddTransition() {
274     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
275
276     // By default states are ordered in the order added.
277     $workflow
278       ->getTypePlugin()
279       ->addState('draft', 'Draft')
280       ->addState('published', 'Published');
281
282     $this->assertFalse($workflow->getTypePlugin()->getState('draft')->canTransitionTo('published'));
283     $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['draft'], 'published');
284     $this->assertTrue($workflow->getTypePlugin()->getState('draft')->canTransitionTo('published'));
285     $this->assertEquals(0, $workflow->getTypePlugin()->getTransition('publish')->weight());
286     $this->assertTrue($workflow->getTypePlugin()->hasTransition('publish'));
287     $this->assertFalse($workflow->getTypePlugin()->hasTransition('draft'));
288
289     $workflow->getTypePlugin()->addTransition('save_publish', 'Save', ['published'], 'published');
290     $this->assertEquals(1, $workflow->getTypePlugin()->getTransition('save_publish')->weight());
291   }
292
293   /**
294    * @covers ::addTransition
295    */
296   public function testAddTransitionDuplicateException() {
297     $this->setExpectedException(\InvalidArgumentException::class, "The transition 'publish' already exists in workflow.");
298     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
299     $workflow->getTypePlugin()->addState('published', 'Published');
300     $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['published'], 'published');
301     $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['published'], 'published');
302   }
303
304   /**
305    * @covers ::addTransition
306    */
307   public function testAddTransitionInvalidIdException() {
308     $this->setExpectedException(\InvalidArgumentException::class, "The transition ID 'publish-publish' must contain only lowercase letters, numbers, and underscores");
309     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
310     $workflow->getTypePlugin()->addState('published', 'Published');
311     $workflow->getTypePlugin()->addTransition('publish-publish', 'Publish', ['published'], 'published');
312   }
313
314   /**
315    * @covers ::addTransition
316    */
317   public function testAddTransitionMissingFromException() {
318     $this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' does not exist in workflow.");
319     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
320     $workflow->getTypePlugin()->addState('published', 'Published');
321     $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['draft'], 'published');
322   }
323
324   /**
325    * @covers ::addTransition
326    */
327   public function testAddTransitionDuplicateTransitionStatesException() {
328     $this->setExpectedException(\InvalidArgumentException::class, "The 'publish' transition already allows 'draft' to 'published' transitions in workflow.");
329     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
330     $workflow
331       ->getTypePlugin()
332       ->addState('draft', 'Draft')
333       ->addState('published', 'Published');
334     $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['draft', 'published'], 'published');
335     $workflow->getTypePlugin()->addTransition('draft_to_published', 'Publish a draft', ['draft'], 'published');
336   }
337
338   /**
339    * @covers ::addTransition
340    */
341   public function testAddTransitionConsistentAfterFromCatch() {
342     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
343     $workflow->getTypePlugin()->addState('published', 'Published');
344     try {
345       $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['draft'], 'published');
346     }
347     catch (\InvalidArgumentException $e) {
348     }
349     // Ensure that the workflow is not left in an inconsistent state after an
350     // exception is thrown from Workflow::setTransitionFromStates() whilst
351     // calling Workflow::addTransition().
352     $this->assertFalse($workflow->getTypePlugin()->hasTransition('publish'));
353   }
354
355   /**
356    * @covers ::addTransition
357    */
358   public function testAddTransitionMissingToException() {
359     $this->setExpectedException(\InvalidArgumentException::class, "The state 'published' does not exist in workflow.");
360     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
361     $workflow->getTypePlugin()->addState('draft', 'Draft');
362     $workflow->getTypePlugin()->addTransition('publish', 'Publish', ['draft'], 'published');
363   }
364
365   /**
366    * @covers ::getTransitions
367    * @covers ::setTransitionWeight
368    */
369   public function testGetTransitions() {
370     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
371
372     // Getting transitions works when there are none.
373     $this->assertArrayEquals([], array_keys($workflow->getTypePlugin()->getTransitions()));
374     $this->assertArrayEquals([], array_keys($workflow->getTypePlugin()->getTransitions([])));
375
376     // By default states are ordered in the order added.
377     $workflow
378       ->getTypePlugin()
379       ->addState('a', 'A')
380       ->addState('b', 'B')
381       ->addTransition('a_b', 'A to B', ['a'], 'b')
382       ->addTransition('a_a', 'A to A', ['a'], 'a');
383
384     // Transitions are stored in alphabetical key order in configuration.
385     $this->assertArrayEquals(['a_a', 'a_b'], array_keys($workflow->getTypePlugin()->getConfiguration()['transitions']));
386
387     // Ensure we're returning transition objects.
388     $this->assertInstanceOf(Transition::class, $workflow->getTypePlugin()->getTransitions()['a_a']);
389
390     // Passing in no IDs returns all transitions.
391     $this->assertArrayEquals(['a_b', 'a_a'], array_keys($workflow->getTypePlugin()->getTransitions()));
392
393     // The order of states is by weight.
394     $workflow->getTypePlugin()->setTransitionWeight('a_a', -1);
395     $this->assertArrayEquals(['a_a', 'a_b'], array_keys($workflow->getTypePlugin()->getTransitions()));
396
397     // If all weights are equal it will fallback to labels.
398     $workflow->getTypePlugin()->setTransitionWeight('a_a', 0);
399     $this->assertArrayEquals(['a_a', 'a_b'], array_keys($workflow->getTypePlugin()->getTransitions()));
400     $workflow->getTypePlugin()->setTransitionLabel('a_b', 'A B');
401     $this->assertArrayEquals(['a_b', 'a_a'], array_keys($workflow->getTypePlugin()->getTransitions()));
402
403     // You can limit the states returned by passing in states IDs.
404     $this->assertArrayEquals(['a_a'], array_keys($workflow->getTypePlugin()->getTransitions(['a_a'])));
405
406     // An empty array does not load all states.
407     $this->assertArrayEquals([], array_keys($workflow->getTypePlugin()->getTransitions([])));
408   }
409
410
411   /**
412    * @covers ::getTransition
413    */
414   public function testGetTransition() {
415     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
416     // By default states are ordered in the order added.
417     $workflow
418       ->getTypePlugin()
419       ->addState('draft', 'Draft')
420       ->addState('published', 'Published')
421       ->addState('archived', 'Archived')
422       ->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft')
423       ->addTransition('publish', 'Publish', ['draft'], 'published');
424
425     // Ensure we're returning state objects and they are set up correctly
426     $this->assertInstanceOf(Transition::class, $workflow->getTypePlugin()->getTransition('create_new_draft'));
427     $this->assertEquals('publish', $workflow->getTypePlugin()->getTransition('publish')->id());
428     $this->assertEquals('Publish', $workflow->getTypePlugin()->getTransition('publish')->label());
429
430     $transition = $workflow->getTypePlugin()->getTransition('publish');
431     $this->assertEquals($workflow->getTypePlugin()->getState('draft'), $transition->from()['draft']);
432     $this->assertEquals($workflow->getTypePlugin()->getState('published'), $transition->to());
433   }
434
435   /**
436    * @covers ::getTransition
437    */
438   public function testGetTransitionException() {
439     $this->setExpectedException(\InvalidArgumentException::class, "The transition 'transition_that_does_not_exist' does not exist in workflow.");
440     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
441     $workflow->getTypePlugin()->getTransition('transition_that_does_not_exist');
442   }
443
444   /**
445    * @covers ::getTransitionsForState
446    */
447   public function testGetTransitionsForState() {
448     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
449     // By default states are ordered in the order added.
450     $workflow
451       ->getTypePlugin()
452       ->addState('draft', 'Draft')
453       ->addState('published', 'Published')
454       ->addState('archived', 'Archived')
455       ->addTransition('create_new_draft', 'Create new draft', ['archived', 'draft'], 'draft')
456       ->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
457       ->addTransition('archive', 'Archive', ['published'], 'archived');
458
459     $this->assertEquals(['create_new_draft', 'publish'], array_keys($workflow->getTypePlugin()->getTransitionsForState('draft')));
460     $this->assertEquals(['create_new_draft'], array_keys($workflow->getTypePlugin()->getTransitionsForState('draft', 'to')));
461     $this->assertEquals(['publish', 'archive'], array_keys($workflow->getTypePlugin()->getTransitionsForState('published')));
462     $this->assertEquals(['publish'], array_keys($workflow->getTypePlugin()->getTransitionsForState('published', 'to')));
463     $this->assertEquals(['create_new_draft'], array_keys($workflow->getTypePlugin()->getTransitionsForState('archived', 'from')));
464     $this->assertEquals(['archive'], array_keys($workflow->getTypePlugin()->getTransitionsForState('archived', 'to')));
465   }
466
467
468   /**
469    * @covers ::getTransitionFromStateToState
470    * @covers ::hasTransitionFromStateToState
471    */
472   public function testGetTransitionFromStateToState() {
473     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
474     // By default states are ordered in the order added.
475     $workflow
476       ->getTypePlugin()
477       ->addState('draft', 'Draft')
478       ->addState('published', 'Published')
479       ->addState('archived', 'Archived')
480       ->addTransition('create_new_draft', 'Create new draft', ['archived', 'draft'], 'draft')
481       ->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
482       ->addTransition('archive', 'Archive', ['published'], 'archived');
483
484     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('draft', 'published'));
485     $this->assertFalse($workflow->getTypePlugin()->hasTransitionFromStateToState('archived', 'archived'));
486     $transition = $workflow->getTypePlugin()->getTransitionFromStateToState('published', 'archived');
487     $this->assertEquals('Archive', $transition->label());
488   }
489
490   /**
491    * @covers ::getTransitionFromStateToState
492    */
493   public function testGetTransitionFromStateToStateException() {
494     $this->setExpectedException(\InvalidArgumentException::class, "The transition from 'archived' to 'archived' does not exist in workflow.");
495     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
496     // By default states are ordered in the order added.
497     $workflow
498       ->getTypePlugin()
499       ->addState('draft', 'Draft')
500       ->addState('published', 'Published')
501       ->addState('archived', 'Archived')
502       ->addTransition('create_new_draft', 'Create new draft', ['archived', 'draft'], 'draft')
503       ->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
504       ->addTransition('archive', 'Archive', ['published'], 'archived');
505
506     $workflow->getTypePlugin()->getTransitionFromStateToState('archived', 'archived');
507   }
508
509   /**
510    * @covers ::setTransitionLabel
511    */
512   public function testSetTransitionLabel() {
513     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
514     $workflow
515       ->getTypePlugin()
516       ->addState('draft', 'Draft')
517       ->addState('published', 'Published')
518       ->addTransition('publish', 'Publish', ['draft'], 'published');
519     $this->assertEquals('Publish', $workflow->getTypePlugin()->getTransition('publish')->label());
520     $workflow->getTypePlugin()->setTransitionLabel('publish', 'Publish!');
521     $this->assertEquals('Publish!', $workflow->getTypePlugin()->getTransition('publish')->label());
522   }
523
524   /**
525    * @covers ::setTransitionLabel
526    */
527   public function testSetTransitionLabelException() {
528     $this->setExpectedException(\InvalidArgumentException::class, "The transition 'draft-published' does not exist in workflow.");
529     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
530     $workflow->getTypePlugin()->addState('published', 'Published');
531     $workflow->getTypePlugin()->setTransitionLabel('draft-published', 'Publish');
532   }
533
534   /**
535    * @covers ::setTransitionWeight
536    */
537   public function testSetTransitionWeight() {
538     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
539     $workflow
540       ->getTypePlugin()
541       ->addState('draft', 'Draft')
542       ->addState('published', 'Published')
543       ->addTransition('publish', 'Publish', ['draft'], 'published');
544     $this->assertEquals(0, $workflow->getTypePlugin()->getTransition('publish')->weight());
545     $workflow->getTypePlugin()->setTransitionWeight('publish', 10);
546     $this->assertEquals(10, $workflow->getTypePlugin()->getTransition('publish')->weight());
547   }
548
549   /**
550    * @covers ::setTransitionWeight
551    */
552   public function testSetTransitionWeightException() {
553     $this->setExpectedException(\InvalidArgumentException::class, "The transition 'draft-published' does not exist in workflow.");
554     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
555     $workflow->getTypePlugin()->addState('published', 'Published');
556     $workflow->getTypePlugin()->setTransitionWeight('draft-published', 10);
557   }
558
559   /**
560    * @covers ::setTransitionFromStates
561    */
562   public function testSetTransitionFromStates() {
563     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
564     $workflow
565       ->getTypePlugin()
566       ->addState('draft', 'Draft')
567       ->addState('published', 'Published')
568       ->addState('archived', 'Archived')
569       ->addTransition('test', 'Test', ['draft'], 'draft');
570
571     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('draft', 'draft'));
572     $this->assertFalse($workflow->getTypePlugin()->hasTransitionFromStateToState('published', 'draft'));
573     $this->assertFalse($workflow->getTypePlugin()->hasTransitionFromStateToState('archived', 'draft'));
574     $workflow->getTypePlugin()->setTransitionFromStates('test', ['draft', 'published', 'archived']);
575     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('draft', 'draft'));
576     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('published', 'draft'));
577     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('archived', 'draft'));
578     $workflow->getTypePlugin()->setTransitionFromStates('test', ['published', 'archived']);
579     $this->assertFalse($workflow->getTypePlugin()->hasTransitionFromStateToState('draft', 'draft'));
580     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('published', 'draft'));
581     $this->assertTrue($workflow->getTypePlugin()->hasTransitionFromStateToState('archived', 'draft'));
582   }
583
584   /**
585    * @covers ::setTransitionFromStates
586    */
587   public function testSetTransitionFromStatesMissingTransition() {
588     $this->setExpectedException(\InvalidArgumentException::class, "The transition 'test' does not exist in workflow.");
589     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
590     $workflow
591       ->getTypePlugin()
592       ->addState('draft', 'Draft')
593       ->addState('published', 'Published')
594       ->addState('archived', 'Archived')
595       ->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft');
596
597     $workflow->getTypePlugin()->setTransitionFromStates('test', ['draft', 'published', 'archived']);
598   }
599
600   /**
601    * @covers ::setTransitionFromStates
602    */
603   public function testSetTransitionFromStatesMissingState() {
604     $this->setExpectedException(\InvalidArgumentException::class, "The state 'published' does not exist in workflow.");
605     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
606     $workflow
607       ->getTypePlugin()
608       ->addState('draft', 'Draft')
609       ->addState('archived', 'Archived')
610       ->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft');
611
612     $workflow->getTypePlugin()->setTransitionFromStates('create_new_draft', ['draft', 'published', 'archived']);
613   }
614
615   /**
616    * @covers ::setTransitionFromStates
617    */
618   public function testSetTransitionFromStatesAlreadyExists() {
619     $this->setExpectedException(\InvalidArgumentException::class, "The 'create_new_draft' transition already allows 'draft' to 'draft' transitions in workflow.");
620     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
621     $workflow
622       ->getTypePlugin()
623       ->addState('draft', 'Draft')
624       ->addState('archived', 'Archived')
625       ->addState('needs_review', 'Needs Review')
626       ->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft')
627       ->addTransition('needs_review', 'Needs review', ['needs_review'], 'draft');
628
629     $workflow->getTypePlugin()->setTransitionFromStates('needs_review', ['draft']);
630   }
631
632   /**
633    * @covers ::deleteTransition
634    */
635   public function testDeleteTransition() {
636     $workflow_type = new TestType([], '', []);
637     $workflow_type
638       ->addState('draft', 'Draft')
639       ->addState('published', 'Published')
640       ->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft')
641       ->addTransition('publish', 'Publish', ['draft'], 'published');
642     $this->assertTrue($workflow_type->getState('draft')->canTransitionTo('published'));
643     $workflow_type->deleteTransition('publish');
644     $this->assertFalse($workflow_type->getState('draft')->canTransitionTo('published'));
645     $this->assertTrue($workflow_type->getState('draft')->canTransitionTo('draft'));
646   }
647
648   /**
649    * @covers ::deleteTransition
650    */
651   public function testDeleteTransitionException() {
652     $this->setExpectedException(\InvalidArgumentException::class, "The transition 'draft-published' does not exist in workflow.");
653     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
654     $workflow->getTypePlugin()->addState('published', 'Published');
655     $workflow->getTypePlugin()->deleteTransition('draft-published');
656   }
657
658   /**
659    * @covers \Drupal\workflows\Entity\Workflow::status
660    */
661   public function testStatus() {
662     $workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
663     $this->assertFalse($workflow->status());
664     $workflow->getTypePlugin()->addState('published', 'Published');
665     $this->assertTrue($workflow->status());
666   }
667
668 }