Update core 8.3.0
This commit is contained in:
parent
da7a7918f8
commit
cd7a898e66
6144 changed files with 132297 additions and 87747 deletions
|
@ -0,0 +1,2 @@
|
|||
workflows.workflow.*.third_party.workflow_third_party_settings_test:
|
||||
type: ignore
|
|
@ -0,0 +1,8 @@
|
|||
name: 'Workflow Third Party Settings Test'
|
||||
type: module
|
||||
description: 'Allows third party settings on workflows to be tested.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- workflows
|
|
@ -0,0 +1,42 @@
|
|||
workflow.type_settings.workflow_type_test:
|
||||
type: mapping
|
||||
label: 'Workflow test type settings'
|
||||
mapping:
|
||||
states:
|
||||
type: sequence
|
||||
sequence:
|
||||
type: ignore
|
||||
|
||||
workflow.type_settings.workflow_type_required_state_test:
|
||||
type: mapping
|
||||
label: 'Workflow test type settings'
|
||||
mapping:
|
||||
states:
|
||||
type: sequence
|
||||
sequence:
|
||||
type: ignore
|
||||
|
||||
workflow.type_settings.workflow_type_complex_test:
|
||||
type: mapping
|
||||
label: 'Workflow complex test type settings'
|
||||
mapping:
|
||||
states:
|
||||
type: sequence
|
||||
label: 'Additional state configuration'
|
||||
sequence:
|
||||
type: mapping
|
||||
label: 'States'
|
||||
mapping:
|
||||
extra:
|
||||
type: string
|
||||
label: 'Extra information'
|
||||
transitions:
|
||||
type: sequence
|
||||
label: 'Additional transition configuration'
|
||||
sequence:
|
||||
type: mapping
|
||||
label: 'Transitions'
|
||||
mapping:
|
||||
extra:
|
||||
type: string
|
||||
label: 'Extra information'
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflow_type_test;
|
||||
|
||||
use Drupal\workflows\StateInterface;
|
||||
|
||||
/**
|
||||
* A value object representing a workflow state.
|
||||
*/
|
||||
class DecoratedState implements StateInterface {
|
||||
|
||||
/**
|
||||
* The vanilla state object from the Workflow module.
|
||||
*
|
||||
* @var \Drupal\workflows\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* Extra information added to state.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $extra;
|
||||
|
||||
/**
|
||||
* DecoratedState constructor.
|
||||
*
|
||||
* @param \Drupal\workflows\StateInterface $state
|
||||
* The vanilla state object from the Workflow module.
|
||||
* @param string $extra
|
||||
* (optional) Extra information stored on the state. Defaults to ''.
|
||||
*/
|
||||
public function __construct(StateInterface $state, $extra = '') {
|
||||
$this->state = $state;
|
||||
$this->extra = $extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the extra information stored on the state.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExtra() {
|
||||
return $this->extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function id() {
|
||||
return $this->state->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function label() {
|
||||
return $this->state->label();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function weight() {
|
||||
return $this->state->weight();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canTransitionTo($to_state_id) {
|
||||
return $this->state->canTransitionTo($to_state_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTransitionTo($to_state_id) {
|
||||
return $this->state->getTransitionTo($to_state_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTransitions() {
|
||||
return $this->state->getTransitions();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflow_type_test;
|
||||
|
||||
use Drupal\workflows\TransitionInterface;
|
||||
|
||||
/**
|
||||
* A value object representing a workflow transition.
|
||||
*/
|
||||
class DecoratedTransition implements TransitionInterface {
|
||||
|
||||
/**
|
||||
* The vanilla transition object from the Workflow module.
|
||||
*
|
||||
* @var \Drupal\workflows\TransitionInterface
|
||||
*/
|
||||
protected $transition;
|
||||
|
||||
/**
|
||||
* Extra information added to transition.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $extra;
|
||||
|
||||
/**
|
||||
* DecoratedTransition constructor.
|
||||
*
|
||||
* @param \Drupal\workflows\TransitionInterface $transition
|
||||
* The vanilla transition object from the Workflow module.
|
||||
* @param string $extra
|
||||
* (optional) Extra information stored on the transition. Defaults to ''.
|
||||
*/
|
||||
public function __construct(TransitionInterface $transition, $extra = '') {
|
||||
$this->transition = $transition;
|
||||
$this->extra = $extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the extra information stored on the transition.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExtra() {
|
||||
return $this->extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function id() {
|
||||
return $this->transition->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function label() {
|
||||
return $this->transition->label();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function from() {
|
||||
return $this->transition->from();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function to() {
|
||||
return $this->transition->to();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function weight() {
|
||||
return $this->transition->weight();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflow_type_test\Plugin\WorkflowType;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\workflows\Plugin\WorkflowTypeBase;
|
||||
use Drupal\workflows\StateInterface;
|
||||
use Drupal\workflows\TransitionInterface;
|
||||
use Drupal\workflows\WorkflowInterface;
|
||||
use Drupal\workflow_type_test\DecoratedState;
|
||||
use Drupal\workflow_type_test\DecoratedTransition;
|
||||
|
||||
/**
|
||||
* Test workflow type.
|
||||
*
|
||||
* @WorkflowType(
|
||||
* id = "workflow_type_complex_test",
|
||||
* label = @Translation("Workflow Type Complex Test"),
|
||||
* )
|
||||
*/
|
||||
class ComplexTestType extends WorkflowTypeBase {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function decorateState(StateInterface $state) {
|
||||
if (isset($this->configuration['states'][$state->id()])) {
|
||||
$state = new DecoratedState($state, $this->configuration['states'][$state->id()]['extra']);
|
||||
}
|
||||
else {
|
||||
$state = new DecoratedState($state);
|
||||
}
|
||||
return $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function decorateTransition(TransitionInterface $transition) {
|
||||
if (isset($this->configuration['transitions'][$transition->id()])) {
|
||||
$transition = new DecoratedTransition($transition, $this->configuration['transitions'][$transition->id()]['extra']);
|
||||
}
|
||||
else {
|
||||
$transition = new DecoratedTransition($transition);
|
||||
}
|
||||
return $transition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildStateConfigurationForm(FormStateInterface $form_state, WorkflowInterface $workflow, StateInterface $state = NULL) {
|
||||
/** @var \Drupal\workflow_type_test\DecoratedState $state */
|
||||
$form = [];
|
||||
$form['extra'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Extra'),
|
||||
'#description' => $this->t('Extra information added to state'),
|
||||
'#default_value' => isset($state) ? $state->getExtra() : FALSE,
|
||||
];
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildTransitionConfigurationForm(FormStateInterface $form_state, WorkflowInterface $workflow, TransitionInterface $transition = NULL) {
|
||||
/** @var \Drupal\workflow_type_test\DecoratedTransition $transition */
|
||||
$form = [];
|
||||
$form['extra'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Extra'),
|
||||
'#description' => $this->t('Extra information added to transition'),
|
||||
'#default_value' => isset($transition) ? $transition->getExtra() : FALSE,
|
||||
];
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onDependencyRemoval(array $dependencies) {
|
||||
// Always return TRUE to allow the logic in
|
||||
// \Drupal\workflows\Entity\Workflow::onDependencyRemoval() to be tested.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflow_type_test\Plugin\WorkflowType;
|
||||
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\workflows\Plugin\WorkflowTypeBase;
|
||||
use Drupal\workflows\WorkflowInterface;
|
||||
|
||||
/**
|
||||
* Test workflow type.
|
||||
*
|
||||
* @WorkflowType(
|
||||
* id = "workflow_type_required_state_test",
|
||||
* label = @Translation("Required State Type Test"),
|
||||
* required_states = {
|
||||
* "fresh",
|
||||
* "rotten",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class RequiredStateTestType extends WorkflowTypeBase {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function initializeWorkflow(WorkflowInterface $workflow) {
|
||||
$workflow
|
||||
->addState('fresh', $this->t('Fresh'))
|
||||
->setStateWeight('fresh', -5)
|
||||
->addState('rotten', $this->t('Rotten'))
|
||||
->addTransition('rot', $this->t('Rot'), ['fresh'], 'rotten');
|
||||
return $workflow;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
// No configuration is stored for the test type.
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\workflow_type_test\Plugin\WorkflowType;
|
||||
|
||||
use Drupal\workflows\Plugin\WorkflowTypeBase;
|
||||
|
||||
/**
|
||||
* Test workflow type.
|
||||
*
|
||||
* @WorkflowType(
|
||||
* id = "workflow_type_test",
|
||||
* label = @Translation("Workflow Type Test"),
|
||||
* )
|
||||
*/
|
||||
class TestType extends WorkflowTypeBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
// No configuration is stored for the test type.
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRequiredStates() {
|
||||
// Normally this is obtained from the annotation but we get from state to
|
||||
// allow dynamic testing.
|
||||
return \Drupal::state()->get('workflow_type_test.required_states', []);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
name: 'Workflow Type Test'
|
||||
type: module
|
||||
description: 'Provides a workflow type plugin for testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- workflows
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\workflows\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests workflow UI when there are no types.
|
||||
*
|
||||
* @group workflows
|
||||
*/
|
||||
class WorkflowUiNoTypeTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['workflows', 'block'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// We're testing local actions.
|
||||
$this->drupalPlaceBlock('local_actions_block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the creation of a workflow through the UI.
|
||||
*/
|
||||
public function testWorkflowUiWithNoType() {
|
||||
$this->drupalLogin($this->createUser(['access administration pages', 'administer workflows']));
|
||||
$this->drupalGet('admin/config/workflow/workflows/add');
|
||||
// There are no workflow types so this should be a 403.
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
$this->drupalGet('admin/config/workflow/workflows');
|
||||
$this->assertSession()->pageTextContains('There are no workflow types available. In order to create workflows you need to install a module that provides a workflow type. For example, the Content Moderation module provides a workflow type that enables workflows for content entities.');
|
||||
$this->assertSession()->pageTextNotContains('Add workflow');
|
||||
|
||||
$this->container->get('module_installer')->install(['workflow_type_test']);
|
||||
// The render cache needs to be cleared because although the cache tags are
|
||||
// correctly set the render cache does not pick it up.
|
||||
\Drupal::cache('render')->deleteAll();
|
||||
|
||||
$this->drupalGet('admin/config/workflow/workflows');
|
||||
$this->assertSession()->pageTextNotContains('There are no workflow types available. In order to create workflows you need to install a module that provides a workflow type. For example, the Content Moderation module provides a workflow type that enables workflows for content entities.');
|
||||
$this->assertSession()->linkExists('Add workflow');
|
||||
$this->assertSession()->pageTextContains('There is no Workflow yet.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\workflows\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Tests workflow creation UI.
|
||||
*
|
||||
* @group workflows
|
||||
*/
|
||||
class WorkflowUiTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['workflows', 'workflow_type_test', 'block'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// We're testing local actions.
|
||||
$this->drupalPlaceBlock('local_actions_block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests route access/permissions.
|
||||
*/
|
||||
public function testAccess() {
|
||||
// Create a minimal workflow for testing.
|
||||
$workflow = Workflow::create(['id' => 'test', 'type' => 'workflow_type_test']);
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
|
||||
->save();
|
||||
|
||||
$paths = [
|
||||
'admin/config/workflow/workflows',
|
||||
'admin/config/workflow/workflows/add',
|
||||
'admin/config/workflow/workflows/manage/test',
|
||||
'admin/config/workflow/workflows/manage/test/delete',
|
||||
'admin/config/workflow/workflows/manage/test/add_state',
|
||||
'admin/config/workflow/workflows/manage/test/state/published',
|
||||
'admin/config/workflow/workflows/manage/test/state/published/delete',
|
||||
'admin/config/workflow/workflows/manage/test/add_transition',
|
||||
'admin/config/workflow/workflows/manage/test/transition/publish',
|
||||
'admin/config/workflow/workflows/manage/test/transition/publish/delete',
|
||||
];
|
||||
|
||||
foreach ($paths as $path) {
|
||||
$this->drupalGet($path);
|
||||
// No access.
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
$this->drupalLogin($this->createUser(['administer workflows']));
|
||||
foreach ($paths as $path) {
|
||||
$this->drupalGet($path);
|
||||
// User has access.
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
}
|
||||
|
||||
// Ensure that default states can not be deleted.
|
||||
\Drupal::state()->set('workflow_type_test.required_states', ['published']);
|
||||
$this->drupalGet('admin/config/workflow/workflows/manage/test/state/published/delete');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
\Drupal::state()->set('workflow_type_test.required_states', []);
|
||||
|
||||
// Delete one of the states and ensure the other test cannot be deleted.
|
||||
$this->drupalGet('admin/config/workflow/workflows/manage/test/state/published/delete');
|
||||
$this->submitForm([], 'Delete');
|
||||
$this->drupalGet('admin/config/workflow/workflows/manage/test/state/draft/delete');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the creation of a workflow through the UI.
|
||||
*/
|
||||
public function testWorkflowCreation() {
|
||||
$workflow_storage = $this->container->get('entity_type.manager')->getStorage('workflow');
|
||||
/** @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$this->drupalLogin($this->createUser(['access administration pages', 'administer workflows']));
|
||||
$this->drupalGet('admin/config/workflow');
|
||||
$this->assertSession()->linkByHrefExists('admin/config/workflow/workflows');
|
||||
$this->clickLink('Workflows');
|
||||
$this->assertSession()->pageTextContains('Workflows');
|
||||
$this->assertSession()->pageTextContains('There is no Workflow yet.');
|
||||
$this->clickLink('Add workflow');
|
||||
$this->submitForm(['label' => 'Test', 'id' => 'test', 'workflow_type' => 'workflow_type_test'], 'Save');
|
||||
$this->assertSession()->pageTextContains('Created the Test Workflow.');
|
||||
$this->assertSession()->addressEquals('admin/config/workflow/workflows/manage/test/add_state');
|
||||
$this->drupalGet('/admin/config/workflow/workflows/manage/test');
|
||||
$this->assertSession()->pageTextContains('This workflow has no states and will be disabled until there is at least one, add a new state.');
|
||||
$this->assertSession()->pageTextContains('There are no states yet.');
|
||||
$this->clickLink('Add a new state');
|
||||
$this->submitForm(['label' => 'Published', 'id' => 'published'], 'Save');
|
||||
$this->assertSession()->pageTextContains('Created Published state.');
|
||||
$workflow = $workflow_storage->loadUnchanged('test');
|
||||
$this->assertFalse($workflow->getState('published')->canTransitionTo('published'), 'No default transition from published to published exists.');
|
||||
|
||||
$this->clickLink('Add a new state');
|
||||
// Don't create a draft to draft transition by default.
|
||||
$this->submitForm(['label' => 'Draft', 'id' => 'draft'], 'Save');
|
||||
$this->assertSession()->pageTextContains('Created Draft state.');
|
||||
$workflow = $workflow_storage->loadUnchanged('test');
|
||||
$this->assertFalse($workflow->getState('draft')->canTransitionTo('draft'), 'Can not transition from draft to draft');
|
||||
|
||||
$this->clickLink('Add a new transition');
|
||||
$this->submitForm(['id' => 'publish', 'label' => 'Publish', 'from[draft]' => 'draft', 'to' => 'published'], 'Save');
|
||||
$this->assertSession()->pageTextContains('Created Publish transition.');
|
||||
$workflow = $workflow_storage->loadUnchanged('test');
|
||||
$this->assertTrue($workflow->getState('draft')->canTransitionTo('published'), 'Can transition from draft to published');
|
||||
|
||||
$this->clickLink('Add a new transition');
|
||||
$this->submitForm(['id' => 'create_new_draft', 'label' => 'Create new draft', 'from[draft]' => 'draft', 'to' => 'draft'], 'Save');
|
||||
$this->assertSession()->pageTextContains('Created Create new draft transition.');
|
||||
$workflow = $workflow_storage->loadUnchanged('test');
|
||||
$this->assertTrue($workflow->getState('draft')->canTransitionTo('draft'), 'Can transition from draft to draft');
|
||||
|
||||
// The fist state to edit on the page should be published.
|
||||
$this->clickLink('Edit');
|
||||
$this->assertSession()->fieldValueEquals('label', 'Published');
|
||||
// Change the label.
|
||||
$this->submitForm(['label' => 'Live'], 'Save');
|
||||
$this->assertSession()->pageTextContains('Saved Live state.');
|
||||
|
||||
// Allow published to draft.
|
||||
$this->clickLink('Edit', 3);
|
||||
$this->submitForm(['from[published]' => 'published'], 'Save');
|
||||
$this->assertSession()->pageTextContains('Saved Create new draft transition.');
|
||||
$workflow = $workflow_storage->loadUnchanged('test');
|
||||
$this->assertTrue($workflow->getState('published')->canTransitionTo('draft'), 'Can transition from published to draft');
|
||||
|
||||
// Try creating a duplicate transition.
|
||||
$this->clickLink('Add a new transition');
|
||||
$this->submitForm(['id' => 'create_new_draft', 'label' => 'Create new draft', 'from[published]' => 'published', 'to' => 'draft'], 'Save');
|
||||
$this->assertSession()->pageTextContains('The machine-readable name is already in use. It must be unique.');
|
||||
// Try creating a transition which duplicates the states of another.
|
||||
$this->submitForm(['id' => 'create_new_draft2', 'label' => 'Create new draft again', 'from[published]' => 'published', 'to' => 'draft'], 'Save');
|
||||
$this->assertSession()->pageTextContains('The transition from Live to Draft already exists.');
|
||||
|
||||
// Create a new transition.
|
||||
$this->submitForm(['id' => 'save_and_publish', 'label' => 'Save and publish', 'from[published]' => 'published', 'to' => 'published'], 'Save');
|
||||
$this->assertSession()->pageTextContains('Created Save and publish transition.');
|
||||
// Edit the new transition and try to add an existing transition.
|
||||
$this->clickLink('Edit', 4);
|
||||
$this->submitForm(['from[draft]' => 'draft'], 'Save');
|
||||
$this->assertSession()->pageTextContains('The transition from Draft to Live already exists.');
|
||||
|
||||
// Delete the transition.
|
||||
$workflow = $workflow_storage->loadUnchanged('test');
|
||||
$this->assertTrue($workflow->hasTransitionFromStateToState('published', 'published'), 'Can transition from published to published');
|
||||
$this->clickLink('Delete');
|
||||
$this->assertSession()->pageTextContains('Are you sure you want to delete Save and publish from Test?');
|
||||
$this->submitForm([], 'Delete');
|
||||
$workflow = $workflow_storage->loadUnchanged('test');
|
||||
$this->assertFalse($workflow->hasTransitionFromStateToState('published', 'published'), 'Cannot transition from published to published');
|
||||
|
||||
// Try creating a duplicate state.
|
||||
$this->drupalGet('admin/config/workflow/workflows/manage/test');
|
||||
$this->clickLink('Add a new state');
|
||||
$this->submitForm(['label' => 'Draft', 'id' => 'draft'], 'Save');
|
||||
$this->assertSession()->pageTextContains('The machine-readable name is already in use. It must be unique.');
|
||||
|
||||
// Ensure that weight changes the state ordering.
|
||||
$workflow = $workflow_storage->loadUnchanged('test');
|
||||
$this->assertEquals('published', $workflow->getInitialState()->id());
|
||||
$this->drupalGet('admin/config/workflow/workflows/manage/test');
|
||||
$this->submitForm(['states[draft][weight]' => '-1'], 'Save');
|
||||
$workflow = $workflow_storage->loadUnchanged('test');
|
||||
$this->assertEquals('draft', $workflow->getInitialState()->id());
|
||||
|
||||
// This will take us to the list of workflows, so we need to edit the
|
||||
// workflow again.
|
||||
$this->clickLink('Edit');
|
||||
|
||||
// Ensure that weight changes the transition ordering.
|
||||
$this->assertEquals(['publish', 'create_new_draft'], array_keys($workflow->getTransitions()));
|
||||
$this->drupalGet('admin/config/workflow/workflows/manage/test');
|
||||
$this->submitForm(['transitions[create_new_draft][weight]' => '-1'], 'Save');
|
||||
$workflow = $workflow_storage->loadUnchanged('test');
|
||||
$this->assertEquals(['create_new_draft', 'publish'], array_keys($workflow->getTransitions()));
|
||||
|
||||
// This will take us to the list of workflows, so we need to edit the
|
||||
// workflow again.
|
||||
$this->clickLink('Edit');
|
||||
|
||||
// Ensure that a delete link for the published state exists before deleting
|
||||
// the draft state.
|
||||
$published_delete_link = Url::fromRoute('entity.workflow.delete_state_form', [
|
||||
'workflow' => $workflow->id(),
|
||||
'workflow_state' => 'published',
|
||||
])->toString();
|
||||
$draft_delete_link = Url::fromRoute('entity.workflow.delete_state_form', [
|
||||
'workflow' => $workflow->id(),
|
||||
'workflow_state' => 'draft',
|
||||
])->toString();
|
||||
$this->assertSession()->elementContains('css', 'tr[data-drupal-selector="edit-states-published"]', 'Delete');
|
||||
$this->assertSession()->linkByHrefExists($published_delete_link);
|
||||
$this->assertSession()->linkByHrefExists($draft_delete_link);
|
||||
|
||||
// Make the published state a default state and ensure it is no longer
|
||||
// linked.
|
||||
\Drupal::state()->set('workflow_type_test.required_states', ['published']);
|
||||
$this->getSession()->reload();
|
||||
$this->assertSession()->linkByHrefNotExists($published_delete_link);
|
||||
$this->assertSession()->linkByHrefExists($draft_delete_link);
|
||||
$this->assertSession()->elementNotContains('css', 'tr[data-drupal-selector="edit-states-published"]', 'Delete');
|
||||
\Drupal::state()->set('workflow_type_test.required_states', []);
|
||||
$this->getSession()->reload();
|
||||
$this->assertSession()->elementContains('css', 'tr[data-drupal-selector="edit-states-published"]', 'Delete');
|
||||
$this->assertSession()->linkByHrefExists($published_delete_link);
|
||||
$this->assertSession()->linkByHrefExists($draft_delete_link);
|
||||
|
||||
// Delete the Draft state.
|
||||
$this->clickLink('Delete');
|
||||
$this->assertSession()->pageTextContains('Are you sure you want to delete Draft from Test?');
|
||||
$this->submitForm([], 'Delete');
|
||||
$this->assertSession()->pageTextContains('State Draft deleted.');
|
||||
$workflow = $workflow_storage->loadUnchanged('test');
|
||||
$this->assertFalse($workflow->hasState('draft'), 'Draft state deleted');
|
||||
$this->assertTrue($workflow->hasState('published'), 'Workflow still has published state');
|
||||
|
||||
// The last state cannot be deleted so the only delete link on the page will
|
||||
// be for the workflow.
|
||||
$this->assertSession()->linkByHrefNotExists($published_delete_link);
|
||||
$this->clickLink('Delete');
|
||||
$this->assertSession()->pageTextContains('Are you sure you want to delete Test?');
|
||||
$this->submitForm([], 'Delete');
|
||||
$this->assertSession()->pageTextContains('Workflow Test deleted.');
|
||||
$this->assertSession()->pageTextContains('There is no Workflow yet.');
|
||||
$this->assertNull($workflow_storage->loadUnchanged('test'), 'The test workflow has been deleted');
|
||||
|
||||
// Ensure that workflow types that implement
|
||||
// \Drupal\workflows\WorkflowTypeInterface::initializeWorkflow() are
|
||||
// initialized correctly.
|
||||
$this->drupalGet('admin/config/workflow/workflows');
|
||||
$this->clickLink('Add workflow');
|
||||
$this->submitForm(['label' => 'Test 2', 'id' => 'test2', 'workflow_type' => 'workflow_type_required_state_test'], 'Save');
|
||||
$this->assertSession()->addressEquals('admin/config/workflow/workflows/manage/test2');
|
||||
$workflow = $workflow_storage->loadUnchanged('test2');
|
||||
$this->assertTrue($workflow->hasState('fresh'), 'The workflow has the "fresh" state');
|
||||
$this->assertTrue($workflow->hasState('rotten'), 'The workflow has the "rotten" state');
|
||||
$this->assertTrue($workflow->hasTransition('rot'), 'The workflow has the "rot" transition');
|
||||
$this->assertSession()->pageTextContains('Fresh');
|
||||
$this->assertSession()->pageTextContains('Rotten');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that workflow types can add form fields to states and transitions.
|
||||
*/
|
||||
public function testWorkflowDecoration() {
|
||||
// Create a minimal workflow for testing.
|
||||
$workflow = Workflow::create(['id' => 'test', 'type' => 'workflow_type_complex_test']);
|
||||
$workflow
|
||||
->addState('published', 'Published')
|
||||
->addTransition('publish', 'Publish', ['published'], 'published')
|
||||
->save();
|
||||
|
||||
$this->assertEquals('', $workflow->getState('published')->getExtra());
|
||||
$this->assertEquals('', $workflow->getTransition('publish')->getExtra());
|
||||
|
||||
$this->drupalLogin($this->createUser(['administer workflows']));
|
||||
|
||||
// Add additional state information when editing.
|
||||
$this->drupalGet('admin/config/workflow/workflows/manage/test/state/published');
|
||||
$this->assertSession()->pageTextContains('Extra information added to state');
|
||||
$this->submitForm(['type_settings[workflow_type_complex_test][extra]' => 'Extra state information'], 'Save');
|
||||
|
||||
// Add additional transition information when editing.
|
||||
$this->drupalGet('admin/config/workflow/workflows/manage/test/transition/publish');
|
||||
$this->assertSession()->pageTextContains('Extra information added to transition');
|
||||
$this->submitForm(['type_settings[workflow_type_complex_test][extra]' => 'Extra transition information'], 'Save');
|
||||
|
||||
$workflow_storage = $this->container->get('entity_type.manager')->getStorage('workflow');
|
||||
/** @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $workflow_storage->loadUnchanged('test');
|
||||
$this->assertEquals('Extra state information', $workflow->getState('published')->getExtra());
|
||||
$this->assertEquals('Extra transition information', $workflow->getTransition('publish')->getExtra());
|
||||
|
||||
// Add additional state information when adding.
|
||||
$this->drupalGet('admin/config/workflow/workflows/manage/test/add_state');
|
||||
$this->assertSession()->pageTextContains('Extra information added to state');
|
||||
$this->submitForm(['label' => 'Draft', 'id' => 'draft', 'type_settings[workflow_type_complex_test][extra]' => 'Extra state information on add'], 'Save');
|
||||
|
||||
// Add additional transition information when adding.
|
||||
$this->drupalGet('admin/config/workflow/workflows/manage/test/add_transition');
|
||||
$this->assertSession()->pageTextContains('Extra information added to transition');
|
||||
$this->submitForm(['id' => 'draft_published', 'label' => 'Publish', 'from[draft]' => 'draft', 'to' => 'published', 'type_settings[workflow_type_complex_test][extra]' => 'Extra transition information on add'], 'Save');
|
||||
|
||||
$workflow = $workflow_storage->loadUnchanged('test');
|
||||
$this->assertEquals('Extra state information on add', $workflow->getState('draft')->getExtra());
|
||||
$this->assertEquals('Extra transition information on add', $workflow->getTransition('draft_published')->getExtra());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\workflows\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
use Drupal\workflow_type_test\DecoratedState;
|
||||
use Drupal\workflow_type_test\DecoratedTransition;
|
||||
|
||||
/**
|
||||
* Workflow entity tests that require modules or storage.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\workflows\Entity\Workflow
|
||||
*
|
||||
* @group workflows
|
||||
*/
|
||||
class ComplexWorkflowTypeTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['workflows', 'workflow_type_test'];
|
||||
|
||||
/**
|
||||
* Tests a workflow type that decorates transitions and states.
|
||||
*
|
||||
* @covers ::getState
|
||||
* @covers ::getTransition
|
||||
*/
|
||||
public function testComplexType() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'workflow_type_complex_test'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft');
|
||||
$this->assertInstanceOf(DecoratedState::class, $workflow->getState('draft'));
|
||||
$this->assertInstanceOf(DecoratedTransition::class, $workflow->getTransition('create_new_draft'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::loadMultipleByType
|
||||
*/
|
||||
public function testLoadMultipleByType() {
|
||||
$workflow1 = new Workflow(['id' => 'test1', 'type' => 'workflow_type_complex_test'], 'workflow');
|
||||
$workflow1->save();
|
||||
$workflow2 = new Workflow(['id' => 'test2', 'type' => 'workflow_type_complex_test'], 'workflow');
|
||||
$workflow2->save();
|
||||
$workflow3 = new Workflow(['id' => 'test3', 'type' => 'workflow_type_test'], 'workflow');
|
||||
$workflow3->save();
|
||||
|
||||
$this->assertEquals(['test1', 'test2'], array_keys(Workflow::loadMultipleByType('workflow_type_complex_test')));
|
||||
$this->assertEquals(['test3'], array_keys(Workflow::loadMultipleByType('workflow_type_test')));
|
||||
$this->assertEquals([], Workflow::loadMultipleByType('a_type_that_does_not_exist'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\workflows\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
use Drupal\workflows\Exception\RequiredStateMissingException;
|
||||
|
||||
/**
|
||||
* Tests Workflow type's required states and configuration initialization.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\workflows\Plugin\WorkflowTypeBase
|
||||
*
|
||||
* @group workflows
|
||||
*/
|
||||
class RequiredStatesTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['workflows', 'workflow_type_test'];
|
||||
|
||||
/**
|
||||
* @covers ::getRequiredStates
|
||||
* @covers ::initializeWorkflow
|
||||
* @covers ::__construct
|
||||
*/
|
||||
public function testGetRequiredStates() {
|
||||
$workflow = new Workflow([
|
||||
'id' => 'test',
|
||||
'type' => 'workflow_type_required_state_test',
|
||||
], 'workflow');
|
||||
$workflow = $workflow->getTypePlugin()->initializeWorkflow($workflow);
|
||||
$workflow->save();
|
||||
$this->assertEquals(['fresh', 'rotten'], $workflow->getTypePlugin()
|
||||
->getRequiredStates());
|
||||
|
||||
// Ensure that the workflow has the default configuration.
|
||||
$this->assertTrue($workflow->hasState('rotten'));
|
||||
$this->assertTrue($workflow->hasState('fresh'));
|
||||
$this->assertTrue($workflow->hasTransitionFromStateToState('fresh', 'rotten'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\workflows\Entity\Workflow::preSave
|
||||
*/
|
||||
public function testDeleteRequiredStateAPI() {
|
||||
$workflow = new Workflow([
|
||||
'id' => 'test',
|
||||
'type' => 'workflow_type_required_state_test',
|
||||
], 'workflow');
|
||||
$workflow = $workflow->getTypePlugin()->initializeWorkflow($workflow);
|
||||
$workflow->save();
|
||||
// Ensure that required states can't be deleted.
|
||||
$this->setExpectedException(RequiredStateMissingException::class, "Required State Type Test' requires states with the ID 'fresh' in workflow 'test'");
|
||||
$workflow->deleteState('fresh')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\workflows\Entity\Workflow::preSave
|
||||
*/
|
||||
public function testNoStatesRequiredStateAPI() {
|
||||
$workflow = new Workflow([
|
||||
'id' => 'test',
|
||||
'type' => 'workflow_type_required_state_test',
|
||||
], 'workflow');
|
||||
$this->setExpectedException(RequiredStateMissingException::class, "Required State Type Test' requires states with the ID 'fresh', 'rotten' in workflow 'test'");
|
||||
$workflow->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that initialized configuration can be changed.
|
||||
*/
|
||||
public function testChangeRequiredStateAPI() {
|
||||
$workflow = new Workflow([
|
||||
'id' => 'test',
|
||||
'type' => 'workflow_type_required_state_test',
|
||||
], 'workflow');
|
||||
$workflow = $workflow->getTypePlugin()->initializeWorkflow($workflow);
|
||||
$workflow->save();
|
||||
|
||||
// Ensure states added by default configuration can be changed.
|
||||
$this->assertEquals('Fresh', $workflow->getState('fresh')->label());
|
||||
$workflow
|
||||
->setStateLabel('fresh', 'Fresher')
|
||||
->save();
|
||||
$this->assertEquals('Fresher', $workflow->getState('fresh')->label());
|
||||
|
||||
// Ensure transitions can be altered.
|
||||
$workflow
|
||||
->addState('cooked', 'Cooked')
|
||||
->setTransitionFromStates('rot', ['fresh', 'cooked'])
|
||||
->save();
|
||||
$this->assertTrue($workflow->hasTransitionFromStateToState('fresh', 'rotten'));
|
||||
$this->assertTrue($workflow->hasTransitionFromStateToState('cooked', 'rotten'));
|
||||
|
||||
$workflow
|
||||
->setTransitionFromStates('rot', ['cooked'])
|
||||
->save();
|
||||
$this->assertFalse($workflow->hasTransitionFromStateToState('fresh', 'rotten'));
|
||||
$this->assertTrue($workflow->hasTransitionFromStateToState('cooked', 'rotten'));
|
||||
|
||||
// Ensure the default configuration does not cause ordering issues.
|
||||
$workflow->addTransition('cook', 'Cook', ['fresh'], 'cooked')->save();
|
||||
$this->assertSame([
|
||||
'cooked',
|
||||
'fresh',
|
||||
'rotten',
|
||||
], array_keys($workflow->get('states')));
|
||||
$this->assertSame([
|
||||
'cook',
|
||||
'rot',
|
||||
], array_keys($workflow->get('transitions')));
|
||||
|
||||
// Ensure that transitions can be deleted.
|
||||
$workflow->deleteTransition('rot')->save();
|
||||
$this->assertFalse($workflow->hasTransition('rot'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\workflows\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Tests configuration dependencies in workflows.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\workflows\Entity\Workflow
|
||||
*
|
||||
* @group workflows
|
||||
*/
|
||||
class WorkflowDependenciesTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system', 'workflows', 'workflow_type_test', 'workflow_third_party_settings_test'];
|
||||
|
||||
/**
|
||||
* Tests \Drupal\workflows\Entity\Workflow::onDependencyRemoval().
|
||||
*/
|
||||
public function testOnDependencyRemoval() {
|
||||
// Create a workflow that has a dependency on a third party setting.
|
||||
$workflow = Workflow::create(['id' => 'test3', 'type' => 'workflow_type_complex_test']);
|
||||
$workflow->setThirdPartySetting('workflow_third_party_settings_test', 'key', 'value');
|
||||
$workflow->save();
|
||||
$this->assertSame(['workflow_third_party_settings_test', 'workflow_type_test'], $workflow->getDependencies()['module']);
|
||||
|
||||
// Uninstall workflow_third_party_settings_test to ensure
|
||||
// \Drupal\workflows\Entity\Workflow::onDependencyRemoval() works as
|
||||
// expected.
|
||||
\Drupal::service('module_installer')->uninstall(['node', 'workflow_third_party_settings_test']);
|
||||
$workflow = \Drupal::entityTypeManager()->getStorage('workflow')->loadUnchanged($workflow->id());
|
||||
$this->assertSame(['workflow_type_test'], $workflow->getDependencies()['module']);
|
||||
}
|
||||
|
||||
}
|
131
web/core/modules/workflows/tests/src/Unit/StateTest.php
Normal file
131
web/core/modules/workflows/tests/src/Unit/StateTest.php
Normal file
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\workflows\Unit;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
use Drupal\workflows\State;
|
||||
use Drupal\workflows\WorkflowInterface;
|
||||
use Drupal\workflows\WorkflowTypeInterface;
|
||||
use Drupal\workflows\WorkflowTypeManager;
|
||||
use Prophecy\Argument;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\workflows\State
|
||||
*
|
||||
* @group workflows
|
||||
*/
|
||||
class StateTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Sets up the Workflow Type manager so that workflow entities can be used.
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create a container so that the plugin manager and workflow type can be
|
||||
// mocked.
|
||||
$container = new ContainerBuilder();
|
||||
$workflow_type = $this->prophesize(WorkflowTypeInterface::class);
|
||||
$workflow_type->decorateState(Argument::any())->willReturnArgument(0);
|
||||
$workflow_type->decorateTransition(Argument::any())->willReturnArgument(0);
|
||||
$workflow_type->deleteState(Argument::any())->willReturn(NULL);
|
||||
$workflow_type->deleteTransition(Argument::any())->willReturn(NULL);
|
||||
$workflow_manager = $this->prophesize(WorkflowTypeManager::class);
|
||||
$workflow_manager->createInstance('test_type', Argument::any())->willReturn($workflow_type->reveal());
|
||||
$container->set('plugin.manager.workflows.type', $workflow_manager->reveal());
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::__construct
|
||||
* @covers ::id
|
||||
* @covers ::label
|
||||
* @covers ::weight
|
||||
*/
|
||||
public function testGetters() {
|
||||
$state = new State(
|
||||
$this->prophesize(WorkflowInterface::class)->reveal(),
|
||||
'draft',
|
||||
'Draft',
|
||||
3
|
||||
);
|
||||
$this->assertEquals('draft', $state->id());
|
||||
$this->assertEquals('Draft', $state->label());
|
||||
$this->assertEquals(3, $state->weight());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::canTransitionTo
|
||||
*/
|
||||
public function testCanTransitionTo() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addTransition('publish', 'Publish', ['draft'], 'published');
|
||||
$state = $workflow->getState('draft');
|
||||
$this->assertTrue($state->canTransitionTo('published'));
|
||||
$this->assertFalse($state->canTransitionTo('some_other_state'));
|
||||
|
||||
$workflow->deleteTransition('publish');
|
||||
$this->assertFalse($state->canTransitionTo('published'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getTransitionTo
|
||||
*/
|
||||
public function testGetTransitionTo() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addTransition('publish', 'Publish', ['draft'], 'published');
|
||||
$state = $workflow->getState('draft');
|
||||
$transition = $state->getTransitionTo('published');
|
||||
$this->assertEquals('Publish', $transition->label());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getTransitionTo
|
||||
*/
|
||||
public function testGetTransitionToException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "Can not transition to 'published' state");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('draft', 'Draft');
|
||||
$state = $workflow->getState('draft');
|
||||
$state->getTransitionTo('published');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getTransitions
|
||||
*/
|
||||
public function testGetTransitions() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addState('archived', 'Archived')
|
||||
->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft')
|
||||
->addTransition('publish', 'Publish', ['draft'], 'published')
|
||||
->addTransition('archive', 'Archive', ['published'], 'archived');
|
||||
$state = $workflow->getState('draft');
|
||||
$transitions = $state->getTransitions();
|
||||
$this->assertCount(2, $transitions);
|
||||
$this->assertEquals('Create new draft', $transitions['create_new_draft']->label());
|
||||
$this->assertEquals('Publish', $transitions['publish']->label());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::labelCallback
|
||||
*/
|
||||
public function testLabelCallback() {
|
||||
$workflow = $this->prophesize(WorkflowInterface::class)->reveal();
|
||||
$states = [
|
||||
new State($workflow, 'draft', 'Draft'),
|
||||
new State($workflow, 'published', 'Published'),
|
||||
];
|
||||
$this->assertEquals(['Draft', 'Published'], array_map([State::class, 'labelCallback'], $states));
|
||||
}
|
||||
|
||||
}
|
71
web/core/modules/workflows/tests/src/Unit/TransitionTest.php
Normal file
71
web/core/modules/workflows/tests/src/Unit/TransitionTest.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\workflows\Unit;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
use Drupal\workflows\Transition;
|
||||
use Drupal\workflows\WorkflowInterface;
|
||||
use Drupal\workflows\WorkflowTypeInterface;
|
||||
use Drupal\workflows\WorkflowTypeManager;
|
||||
use Prophecy\Argument;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\workflows\Transition
|
||||
*
|
||||
* @group workflows
|
||||
*/
|
||||
class TransitionTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Sets up the Workflow Type manager so that workflow entities can be used.
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create a container so that the plugin manager and workflow type can be
|
||||
// mocked.
|
||||
$container = new ContainerBuilder();
|
||||
$workflow_type = $this->prophesize(WorkflowTypeInterface::class);
|
||||
$workflow_type->decorateState(Argument::any())->willReturnArgument(0);
|
||||
$workflow_type->decorateTransition(Argument::any())->willReturnArgument(0);
|
||||
$workflow_manager = $this->prophesize(WorkflowTypeManager::class);
|
||||
$workflow_manager->createInstance('test_type', Argument::any())->willReturn($workflow_type->reveal());
|
||||
$container->set('plugin.manager.workflows.type', $workflow_manager->reveal());
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::__construct
|
||||
* @covers ::id
|
||||
* @covers ::label
|
||||
*/
|
||||
public function testGetters() {
|
||||
$state = new Transition(
|
||||
$this->prophesize(WorkflowInterface::class)->reveal(),
|
||||
'draft_published',
|
||||
'Publish',
|
||||
['draft'],
|
||||
'published'
|
||||
);
|
||||
$this->assertEquals('draft_published', $state->id());
|
||||
$this->assertEquals('Publish', $state->label());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::from
|
||||
* @covers ::to
|
||||
*/
|
||||
public function testFromAndTo() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addTransition('publish', 'Publish', ['draft'], 'published');
|
||||
$state = $workflow->getState('draft');
|
||||
$transition = $state->getTransitionTo('published');
|
||||
$this->assertEquals($state, $transition->from()['draft']);
|
||||
$this->assertEquals($workflow->getState('published'), $transition->to());
|
||||
}
|
||||
|
||||
}
|
679
web/core/modules/workflows/tests/src/Unit/WorkflowTest.php
Normal file
679
web/core/modules/workflows/tests/src/Unit/WorkflowTest.php
Normal file
|
@ -0,0 +1,679 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\workflows\Unit;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
use Drupal\workflows\State;
|
||||
use Drupal\workflows\Transition;
|
||||
use Drupal\workflows\WorkflowTypeInterface;
|
||||
use Drupal\workflows\WorkflowTypeManager;
|
||||
use Prophecy\Argument;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\workflows\Entity\Workflow
|
||||
*
|
||||
* @group workflows
|
||||
*/
|
||||
class WorkflowTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create a container so that the plugin manager and workflow type can be
|
||||
// mocked.
|
||||
$container = new ContainerBuilder();
|
||||
$workflow_type = $this->prophesize(WorkflowTypeInterface::class);
|
||||
$workflow_type->decorateState(Argument::any())->willReturnArgument(0);
|
||||
$workflow_type->decorateTransition(Argument::any())->willReturnArgument(0);
|
||||
$workflow_manager = $this->prophesize(WorkflowTypeManager::class);
|
||||
$workflow_manager->createInstance('test_type', Argument::any())->willReturn($workflow_type->reveal());
|
||||
$container->set('plugin.manager.workflows.type', $workflow_manager->reveal());
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addState
|
||||
* @covers ::hasState
|
||||
*/
|
||||
public function testAddAndHasState() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$this->assertFalse($workflow->hasState('draft'));
|
||||
|
||||
// By default states are ordered in the order added.
|
||||
$workflow->addState('draft', 'Draft');
|
||||
$this->assertTrue($workflow->hasState('draft'));
|
||||
$this->assertFalse($workflow->hasState('published'));
|
||||
$this->assertEquals(0, $workflow->getState('draft')->weight());
|
||||
// Adding a state does not set up a transition to itself.
|
||||
$this->assertFalse($workflow->hasTransitionFromStateToState('draft', 'draft'));
|
||||
|
||||
// New states are added with a new weight 1 more than the current highest
|
||||
// weight.
|
||||
$workflow->addState('published', 'Published');
|
||||
$this->assertEquals(1, $workflow->getState('published')->weight());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addState
|
||||
*/
|
||||
public function testAddStateException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' already exists in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('draft', 'Draft');
|
||||
$workflow->addState('draft', 'Draft');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addState
|
||||
*/
|
||||
public function testAddStateInvalidIdException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The state ID 'draft-draft' must contain only lowercase letters, numbers, and underscores");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('draft-draft', 'Draft');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getStates
|
||||
*/
|
||||
public function testGetStates() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
|
||||
// Getting states works when there are none.
|
||||
$this->assertArrayEquals([], array_keys($workflow->getStates()));
|
||||
$this->assertArrayEquals([], array_keys($workflow->getStates([])));
|
||||
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addState('archived', 'Archived');
|
||||
|
||||
// States are stored in alphabetical key order.
|
||||
$this->assertArrayEquals([
|
||||
'archived',
|
||||
'draft',
|
||||
'published',
|
||||
], array_keys($workflow->get('states')));
|
||||
|
||||
// Ensure we're returning state objects.
|
||||
$this->assertInstanceOf(State::class, $workflow->getStates()['draft']);
|
||||
|
||||
// Passing in no IDs returns all states.
|
||||
$this->assertArrayEquals(['draft', 'published', 'archived'], array_keys($workflow->getStates()));
|
||||
|
||||
// The order of states is by weight.
|
||||
$workflow->setStateWeight('published', -1);
|
||||
$this->assertArrayEquals(['published', 'draft', 'archived'], array_keys($workflow->getStates()));
|
||||
|
||||
// The label is also used for sorting if weights are equal.
|
||||
$workflow->setStateWeight('archived', 0);
|
||||
$this->assertArrayEquals(['published', 'archived', 'draft'], array_keys($workflow->getStates()));
|
||||
|
||||
// You can limit the states returned by passing in states IDs.
|
||||
$this->assertArrayEquals(['archived', 'draft'], array_keys($workflow->getStates(['draft', 'archived'])));
|
||||
|
||||
// An empty array does not load all states.
|
||||
$this->assertArrayEquals([], array_keys($workflow->getStates([])));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getStates
|
||||
*/
|
||||
public function testGetStatesException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The state 'state_that_does_not_exist' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->getStates(['state_that_does_not_exist']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getState
|
||||
*/
|
||||
public function testGetState() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
// By default states are ordered in the order added.
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addState('archived', 'Archived')
|
||||
->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft')
|
||||
->addTransition('publish', 'Publish', ['draft'], 'published');
|
||||
|
||||
// Ensure we're returning state objects and they are set up correctly
|
||||
$this->assertInstanceOf(State::class, $workflow->getState('draft'));
|
||||
$this->assertEquals('archived', $workflow->getState('archived')->id());
|
||||
$this->assertEquals('Archived', $workflow->getState('archived')->label());
|
||||
|
||||
$draft = $workflow->getState('draft');
|
||||
$this->assertTrue($draft->canTransitionTo('draft'));
|
||||
$this->assertTrue($draft->canTransitionTo('published'));
|
||||
$this->assertFalse($draft->canTransitionTo('archived'));
|
||||
$this->assertEquals('Publish', $draft->getTransitionTo('published')->label());
|
||||
$this->assertEquals(0, $draft->weight());
|
||||
$this->assertEquals(1, $workflow->getState('published')->weight());
|
||||
$this->assertEquals(2, $workflow->getState('archived')->weight());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getState
|
||||
*/
|
||||
public function testGetStateException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The state 'state_that_does_not_exist' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->getState('state_that_does_not_exist');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setStateLabel
|
||||
*/
|
||||
public function testSetStateLabel() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('draft', 'Draft');
|
||||
$this->assertEquals('Draft', $workflow->getState('draft')->label());
|
||||
$workflow->setStateLabel('draft', 'Unpublished');
|
||||
$this->assertEquals('Unpublished', $workflow->getState('draft')->label());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setStateLabel
|
||||
*/
|
||||
public function testSetStateLabelException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->setStateLabel('draft', 'Draft');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setStateWeight
|
||||
*/
|
||||
public function testSetStateWeight() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('draft', 'Draft');
|
||||
$this->assertEquals(0, $workflow->getState('draft')->weight());
|
||||
$workflow->setStateWeight('draft', -10);
|
||||
$this->assertEquals(-10, $workflow->getState('draft')->weight());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setStateWeight
|
||||
*/
|
||||
public function testSetStateWeightException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->setStateWeight('draft', 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::deleteState
|
||||
*/
|
||||
public function testDeleteState() {
|
||||
// Create a container so that the plugin manager and workflow type can be
|
||||
// mocked and test that
|
||||
// \Drupal\workflows\WorkflowTypeInterface::deleteState() is called
|
||||
// correctly.
|
||||
$container = new ContainerBuilder();
|
||||
$workflow_type = $this->prophesize(WorkflowTypeInterface::class);
|
||||
$workflow_type->decorateState(Argument::any())->willReturnArgument(0);
|
||||
$workflow_type->decorateTransition(Argument::any())->willReturnArgument(0);
|
||||
$workflow_type->deleteState('draft')->shouldBeCalled();
|
||||
$workflow_type->deleteTransition('create_new_draft')->shouldBeCalled();
|
||||
$workflow_manager = $this->prophesize(WorkflowTypeManager::class);
|
||||
$workflow_manager->createInstance('test_type', Argument::any())->willReturn($workflow_type->reveal());
|
||||
$container->set('plugin.manager.workflows.type', $workflow_manager->reveal());
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
|
||||
->addTransition('create_new_draft', 'Create new draft', ['draft', 'published'], 'draft');
|
||||
$this->assertCount(2, $workflow->getStates());
|
||||
$this->assertCount(2, $workflow->getState('published')->getTransitions());
|
||||
$workflow->deleteState('draft');
|
||||
$this->assertFalse($workflow->hasState('draft'));
|
||||
$this->assertCount(1, $workflow->getStates());
|
||||
$this->assertCount(1, $workflow->getState('published')->getTransitions());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::deleteState
|
||||
*/
|
||||
public function testDeleteStateException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->deleteState('draft');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::deleteState
|
||||
*/
|
||||
public function testDeleteOnlyStateException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' can not be deleted from workflow 'test' as it is the only state");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('draft', 'Draft');
|
||||
$workflow->deleteState('draft');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getInitialState
|
||||
*/
|
||||
public function testGetInitialState() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
|
||||
// By default states are ordered in the order added.
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addState('archived', 'Archived');
|
||||
|
||||
$this->assertEquals('draft', $workflow->getInitialState()->id());
|
||||
|
||||
// Make published the first state.
|
||||
$workflow->setStateWeight('published', -1);
|
||||
$this->assertEquals('published', $workflow->getInitialState()->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addTransition
|
||||
* @covers ::hasTransition
|
||||
*/
|
||||
public function testAddTransition() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
|
||||
// By default states are ordered in the order added.
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published');
|
||||
|
||||
$this->assertFalse($workflow->getState('draft')->canTransitionTo('published'));
|
||||
$workflow->addTransition('publish', 'Publish', ['draft'], 'published');
|
||||
$this->assertTrue($workflow->getState('draft')->canTransitionTo('published'));
|
||||
$this->assertEquals(0, $workflow->getTransition('publish')->weight());
|
||||
$this->assertTrue($workflow->hasTransition('publish'));
|
||||
$this->assertFalse($workflow->hasTransition('draft'));
|
||||
|
||||
$workflow->addTransition('save_publish', 'Save', ['published'], 'published');
|
||||
$this->assertEquals(1, $workflow->getTransition('save_publish')->weight());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addTransition
|
||||
*/
|
||||
public function testAddTransitionDuplicateException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The transition 'publish' already exists in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('published', 'Published');
|
||||
$workflow->addTransition('publish', 'Publish', ['published'], 'published');
|
||||
$workflow->addTransition('publish', 'Publish', ['published'], 'published');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addTransition
|
||||
*/
|
||||
public function testAddTransitionInvalidIdException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The transition ID 'publish-publish' must contain only lowercase letters, numbers, and underscores");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('published', 'Published');
|
||||
$workflow->addTransition('publish-publish', 'Publish', ['published'], 'published');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addTransition
|
||||
*/
|
||||
public function testAddTransitionMissingFromException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The state 'draft' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('published', 'Published');
|
||||
$workflow->addTransition('publish', 'Publish', ['draft'], 'published');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addTransition
|
||||
*/
|
||||
public function testAddTransitionDuplicateTransitionStatesException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The 'publish' transition already allows 'draft' to 'published' transitions in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published');
|
||||
$workflow->addTransition('publish', 'Publish', ['draft', 'published'], 'published');
|
||||
$workflow->addTransition('draft_to_published', 'Publish a draft', ['draft'], 'published');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addTransition
|
||||
*/
|
||||
public function testAddTransitionConsistentAfterFromCatch() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('published', 'Published');
|
||||
try {
|
||||
$workflow->addTransition('publish', 'Publish', ['draft'], 'published');
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
}
|
||||
// Ensure that the workflow is not left in an inconsistent state after an
|
||||
// exception is thrown from Workflow::setTransitionFromStates() whilst
|
||||
// calling Workflow::addTransition().
|
||||
$this->assertFalse($workflow->hasTransition('publish'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addTransition
|
||||
*/
|
||||
public function testAddTransitionMissingToException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The state 'published' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('draft', 'Draft');
|
||||
$workflow->addTransition('publish', 'Publish', ['draft'], 'published');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getTransitions
|
||||
* @covers ::setTransitionWeight
|
||||
*/
|
||||
public function testGetTransitions() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
|
||||
// Getting transitions works when there are none.
|
||||
$this->assertArrayEquals([], array_keys($workflow->getTransitions()));
|
||||
$this->assertArrayEquals([], array_keys($workflow->getTransitions([])));
|
||||
|
||||
// By default states are ordered in the order added.
|
||||
$workflow
|
||||
->addState('a', 'A')
|
||||
->addState('b', 'B')
|
||||
->addTransition('a_b', 'A to B', ['a'], 'b')
|
||||
->addTransition('a_a', 'A to A', ['a'], 'a');
|
||||
|
||||
// Transitions are stored in alphabetical key order in configuration.
|
||||
$this->assertArrayEquals(['a_a', 'a_b'], array_keys($workflow->get('transitions')));
|
||||
|
||||
// Ensure we're returning transition objects.
|
||||
$this->assertInstanceOf(Transition::class, $workflow->getTransitions()['a_a']);
|
||||
|
||||
// Passing in no IDs returns all transitions.
|
||||
$this->assertArrayEquals(['a_b', 'a_a'], array_keys($workflow->getTransitions()));
|
||||
|
||||
// The order of states is by weight.
|
||||
$workflow->setTransitionWeight('a_a', -1);
|
||||
$this->assertArrayEquals(['a_a', 'a_b'], array_keys($workflow->getTransitions()));
|
||||
|
||||
// If all weights are equal it will fallback to labels.
|
||||
$workflow->setTransitionWeight('a_a', 0);
|
||||
$this->assertArrayEquals(['a_a', 'a_b'], array_keys($workflow->getTransitions()));
|
||||
$workflow->setTransitionLabel('a_b', 'A B');
|
||||
$this->assertArrayEquals(['a_b', 'a_a'], array_keys($workflow->getTransitions()));
|
||||
|
||||
// You can limit the states returned by passing in states IDs.
|
||||
$this->assertArrayEquals(['a_a'], array_keys($workflow->getTransitions(['a_a'])));
|
||||
|
||||
// An empty array does not load all states.
|
||||
$this->assertArrayEquals([], array_keys($workflow->getTransitions([])));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @covers ::getTransition
|
||||
*/
|
||||
public function testGetTransition() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
// By default states are ordered in the order added.
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addState('archived', 'Archived')
|
||||
->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft')
|
||||
->addTransition('publish', 'Publish', ['draft'], 'published');
|
||||
|
||||
// Ensure we're returning state objects and they are set up correctly
|
||||
$this->assertInstanceOf(Transition::class, $workflow->getTransition('create_new_draft'));
|
||||
$this->assertEquals('publish', $workflow->getTransition('publish')->id());
|
||||
$this->assertEquals('Publish', $workflow->getTransition('publish')->label());
|
||||
|
||||
$transition = $workflow->getTransition('publish');
|
||||
$this->assertEquals($workflow->getState('draft'), $transition->from()['draft']);
|
||||
$this->assertEquals($workflow->getState('published'), $transition->to());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getTransition
|
||||
*/
|
||||
public function testGetTransitionException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The transition 'transition_that_does_not_exist' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->getTransition('transition_that_does_not_exist');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getTransitionsForState
|
||||
*/
|
||||
public function testGetTransitionsForState() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
// By default states are ordered in the order added.
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addState('archived', 'Archived')
|
||||
->addTransition('create_new_draft', 'Create new draft', ['archived', 'draft'], 'draft')
|
||||
->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
|
||||
->addTransition('archive', 'Archive', ['published'], 'archived');
|
||||
|
||||
$this->assertEquals(['create_new_draft', 'publish'], array_keys($workflow->getTransitionsForState('draft')));
|
||||
$this->assertEquals(['create_new_draft'], array_keys($workflow->getTransitionsForState('draft', 'to')));
|
||||
$this->assertEquals(['publish', 'archive'], array_keys($workflow->getTransitionsForState('published')));
|
||||
$this->assertEquals(['publish'], array_keys($workflow->getTransitionsForState('published', 'to')));
|
||||
$this->assertEquals(['create_new_draft'], array_keys($workflow->getTransitionsForState('archived', 'from')));
|
||||
$this->assertEquals(['archive'], array_keys($workflow->getTransitionsForState('archived', 'to')));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @covers ::getTransitionFromStateToState
|
||||
* @covers ::hasTransitionFromStateToState
|
||||
*/
|
||||
public function testGetTransitionFromStateToState() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
// By default states are ordered in the order added.
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addState('archived', 'Archived')
|
||||
->addTransition('create_new_draft', 'Create new draft', ['archived', 'draft'], 'draft')
|
||||
->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
|
||||
->addTransition('archive', 'Archive', ['published'], 'archived');
|
||||
|
||||
$this->assertTrue($workflow->hasTransitionFromStateToState('draft', 'published'));
|
||||
$this->assertFalse($workflow->hasTransitionFromStateToState('archived', 'archived'));
|
||||
$transition = $workflow->getTransitionFromStateToState('published', 'archived');
|
||||
$this->assertEquals('Archive', $transition->label());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getTransitionFromStateToState
|
||||
*/
|
||||
public function testGetTransitionFromStateToStateException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The transition from 'archived' to 'archived' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
// By default states are ordered in the order added.
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addState('archived', 'Archived')
|
||||
->addTransition('create_new_draft', 'Create new draft', ['archived', 'draft'], 'draft')
|
||||
->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
|
||||
->addTransition('archive', 'Archive', ['published'], 'archived');
|
||||
|
||||
$workflow->getTransitionFromStateToState('archived', 'archived');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setTransitionLabel
|
||||
*/
|
||||
public function testSetTransitionLabel() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addTransition('publish', 'Publish', ['draft'], 'published');
|
||||
$this->assertEquals('Publish', $workflow->getTransition('publish')->label());
|
||||
$workflow->setTransitionLabel('publish', 'Publish!');
|
||||
$this->assertEquals('Publish!', $workflow->getTransition('publish')->label());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setTransitionLabel
|
||||
*/
|
||||
public function testSetTransitionLabelException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The transition 'draft-published' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('published', 'Published');
|
||||
$workflow->setTransitionLabel('draft-published', 'Publish');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setTransitionWeight
|
||||
*/
|
||||
public function testSetTransitionWeight() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addTransition('publish', 'Publish', ['draft'], 'published');
|
||||
$this->assertEquals(0, $workflow->getTransition('publish')->weight());
|
||||
$workflow->setTransitionWeight('publish', 10);
|
||||
$this->assertEquals(10, $workflow->getTransition('publish')->weight());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setTransitionWeight
|
||||
*/
|
||||
public function testSetTransitionWeightException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The transition 'draft-published' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('published', 'Published');
|
||||
$workflow->setTransitionWeight('draft-published', 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setTransitionFromStates
|
||||
*/
|
||||
public function testSetTransitionFromStates() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addState('archived', 'Archived')
|
||||
->addTransition('test', 'Test', ['draft'], 'draft');
|
||||
|
||||
$this->assertTrue($workflow->hasTransitionFromStateToState('draft', 'draft'));
|
||||
$this->assertFalse($workflow->hasTransitionFromStateToState('published', 'draft'));
|
||||
$this->assertFalse($workflow->hasTransitionFromStateToState('archived', 'draft'));
|
||||
$workflow->setTransitionFromStates('test', ['draft', 'published', 'archived']);
|
||||
$this->assertTrue($workflow->hasTransitionFromStateToState('draft', 'draft'));
|
||||
$this->assertTrue($workflow->hasTransitionFromStateToState('published', 'draft'));
|
||||
$this->assertTrue($workflow->hasTransitionFromStateToState('archived', 'draft'));
|
||||
$workflow->setTransitionFromStates('test', ['published', 'archived']);
|
||||
$this->assertFalse($workflow->hasTransitionFromStateToState('draft', 'draft'));
|
||||
$this->assertTrue($workflow->hasTransitionFromStateToState('published', 'draft'));
|
||||
$this->assertTrue($workflow->hasTransitionFromStateToState('archived', 'draft'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setTransitionFromStates
|
||||
*/
|
||||
public function testSetTransitionFromStatesMissingTransition() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The transition 'test' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addState('archived', 'Archived')
|
||||
->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft');
|
||||
|
||||
$workflow->setTransitionFromStates('test', ['draft', 'published', 'archived']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setTransitionFromStates
|
||||
*/
|
||||
public function testSetTransitionFromStatesMissingState() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The state 'published' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('archived', 'Archived')
|
||||
->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft');
|
||||
|
||||
$workflow->setTransitionFromStates('create_new_draft', ['draft', 'published', 'archived']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setTransitionFromStates
|
||||
*/
|
||||
public function testSetTransitionFromStatesAlreadyExists() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The 'create_new_draft' transition already allows 'draft' to 'draft' transitions in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('archived', 'Archived')
|
||||
->addState('needs_review', 'Needs Review')
|
||||
->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft')
|
||||
->addTransition('needs_review', 'Needs review', ['needs_review'], 'draft');
|
||||
|
||||
$workflow->setTransitionFromStates('needs_review', ['draft']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::deleteTransition
|
||||
*/
|
||||
public function testDeleteTransition() {
|
||||
// Create a container so that the plugin manager and workflow type can be
|
||||
// mocked and test that
|
||||
// \Drupal\workflows\WorkflowTypeInterface::deleteState() is called
|
||||
// correctly.
|
||||
$container = new ContainerBuilder();
|
||||
$workflow_type = $this->prophesize(WorkflowTypeInterface::class);
|
||||
$workflow_type->decorateState(Argument::any())->willReturnArgument(0);
|
||||
$workflow_type->decorateTransition(Argument::any())->willReturnArgument(0);
|
||||
$workflow_type->deleteTransition('publish')->shouldBeCalled();
|
||||
$workflow_manager = $this->prophesize(WorkflowTypeManager::class);
|
||||
$workflow_manager->createInstance('test_type', Argument::any())->willReturn($workflow_type->reveal());
|
||||
$container->set('plugin.manager.workflows.type', $workflow_manager->reveal());
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow
|
||||
->addState('draft', 'Draft')
|
||||
->addState('published', 'Published')
|
||||
->addTransition('create_new_draft', 'Create new draft', ['draft'], 'draft')
|
||||
->addTransition('publish', 'Publish', ['draft'], 'published');
|
||||
$this->assertTrue($workflow->getState('draft')->canTransitionTo('published'));
|
||||
$workflow->deleteTransition('publish');
|
||||
$this->assertFalse($workflow->getState('draft')->canTransitionTo('published'));
|
||||
$this->assertTrue($workflow->getState('draft')->canTransitionTo('draft'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::deleteTransition
|
||||
*/
|
||||
public function testDeleteTransitionException() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, "The transition 'draft-published' does not exist in workflow 'test'");
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$workflow->addState('published', 'Published');
|
||||
$workflow->deleteTransition('draft-published');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::status
|
||||
*/
|
||||
public function testStatus() {
|
||||
$workflow = new Workflow(['id' => 'test', 'type' => 'test_type'], 'workflow');
|
||||
$this->assertFalse($workflow->status());
|
||||
$workflow->addState('published', 'Published');
|
||||
$this->assertTrue($workflow->status());
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue