WorkspacesContentModerationStateTest.php
Same filename in other branches
Namespace
Drupal\Tests\content_moderation\KernelFile
-
core/
modules/ content_moderation/ tests/ src/ Kernel/ WorkspacesContentModerationStateTest.php
View source
<?php
declare (strict_types=1);
namespace Drupal\Tests\content_moderation\Kernel;
use Drupal\Core\Entity\EntityInterface;
use Drupal\entity_test\Entity\EntityTestMulRevPub;
use Drupal\node\Entity\Node;
use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
use Drupal\Tests\user\Traits\UserCreationTrait;
use Drupal\Tests\workspaces\Kernel\WorkspaceTestTrait;
use Drupal\workflows\Entity\Workflow;
use Drupal\workflows\WorkflowInterface;
use Drupal\workspaces\WorkspacePublishException;
/**
* Tests that Workspaces and Content Moderation work together properly.
*
* @group content_moderation
* @group workspaces
* @group #slow
*/
class WorkspacesContentModerationStateTest extends ContentModerationStateTest {
use ContentModerationTestTrait {
createEditorialWorkflow as traitCreateEditorialWorkflow;
addEntityTypeAndBundleToWorkflow as traitAddEntityTypeAndBundleToWorkflow;
}
use ContentTypeCreationTrait {
createContentType as traitCreateContentType;
}
use UserCreationTrait;
use WorkspaceTestTrait;
/**
* The ID of the revisionable entity type used in the tests.
*
* @var string
*/
protected $revEntityTypeId = 'entity_test_revpub';
const SKIP_METHODS = [
// This test creates published default revisions in Live, which can not be
// deleted in a workspace. A test scenario for the case when Content
// Moderation and Workspaces are used together is covered in
// parent::testContentModerationStateRevisionDataRemoval().
'testContentModerationStateDataRemoval',
// This test does not assert anything that can be workspace-specific.
'testModerationWithFieldConfigOverride',
// This test does not assert anything that can be workspace-specific.
'testWorkflowDependencies',
// This test does not assert anything that can be workspace-specific.
'testWorkflowNonConfigBundleDependencies',
// This test does not assert anything that can be workspace-specific.
'testGetCurrentUserId',
];
/**
* {@inheritdoc}
*/
protected function setUp() : void {
if (in_array($this->name(), static::SKIP_METHODS, TRUE)) {
$this->markTestSkipped('Irrelevant for this test');
}
parent::setUp();
$this->initializeWorkspacesModule();
$this->switchToWorkspace('stage');
}
/**
* Tests that the 'workspace' entity type can not be moderated.
*
* @see \Drupal\workspaces\EntityTypeInfo::entityTypeAlter()
*/
public function testWorkspaceEntityTypeModeration() : void {
/** @var \Drupal\content_moderation\ModerationInformationInterface $moderation_info */
$moderation_info = \Drupal::service('content_moderation.moderation_information');
$entity_type = \Drupal::entityTypeManager()->getDefinition('workspace');
$this->assertFalse($moderation_info->canModerateEntitiesOfEntityType($entity_type));
}
/**
* Tests the integration between Content Moderation and Workspaces.
*/
public function testContentModerationIntegrationWithWorkspaces() : void {
$editorial = $this->createEditorialWorkflow();
$access_handler = \Drupal::entityTypeManager()->getAccessControlHandler('workspace');
// Create another workflow which has the same states as the 'editorial' one,
// but it doesn't create default revisions for the 'archived' state. This
// covers the case when two bundles of the same entity type use different
// workflows with same moderation state names but with different settings.
$editorial_2_values = $editorial->toArray();
unset($editorial_2_values['uuid']);
$editorial_2_values['id'] = 'editorial_2';
$editorial_2_values['type_settings']['states']['archived']['default_revision'] = FALSE;
$editorial_2 = Workflow::create($editorial_2_values);
$this->workspaceManager
->executeOutsideWorkspace(function () use ($editorial_2) {
$editorial_2->save();
});
// Create two bundles and assign the two workflows for each of them.
$this->createContentType([
'type' => 'page',
]);
$this->addEntityTypeAndBundleToWorkflow($editorial, 'node', 'page');
$this->createContentType([
'type' => 'article',
]);
$this->addEntityTypeAndBundleToWorkflow($editorial_2, 'node', 'article');
// Create three entities for each bundle, covering all the available
// moderation states.
$page_archived = Node::create([
'type' => 'page',
'title' => 'Test page - archived',
'moderation_state' => 'archived',
]);
$page_archived->save();
$page_draft = Node::create([
'type' => 'page',
'title' => 'Test page - draft',
'moderation_state' => 'draft',
]);
$page_draft->save();
$page_published = Node::create([
'type' => 'page',
'title' => 'Test page - published',
'moderation_state' => 'published',
]);
$page_published->save();
$article_archived = Node::create([
'type' => 'article',
'title' => 'Test article - archived',
'moderation_state' => 'archived',
]);
$article_archived->save();
$article_draft = Node::create([
'type' => 'article',
'title' => 'Test article - draft',
'moderation_state' => 'draft',
]);
$article_draft->save();
$article_published = Node::create([
'type' => 'article',
'title' => 'Test article - published',
'moderation_state' => 'published',
]);
$article_published->save();
// We have three items in a non-default moderation state:
// - $page_draft
// - $article_archived
// - $article_draft
// Therefore the workspace can not be published.
// This assertion also covers two moderation states from different workflows
// with the same name ('archived'), but with different default revision
// settings.
try {
$this->workspaces['stage']
->publish();
$this->fail('The expected exception was not thrown.');
} catch (WorkspacePublishException $e) {
$this->assertEquals('The Stage workspace can not be published because it contains 3 items in an unpublished moderation state.', $e->getMessage());
}
// Get the $page_draft node to a publishable state and try again.
$page_draft->moderation_state->value = 'published';
$page_draft->save();
try {
$access_handler->resetCache();
$this->workspaces['stage']
->publish();
$this->fail('The expected exception was not thrown.');
} catch (WorkspacePublishException $e) {
$this->assertEquals('The Stage workspace can not be published because it contains 2 items in an unpublished moderation state.', $e->getMessage());
}
// Get the $article_archived node to a publishable state and try again.
$article_archived->moderation_state->value = 'published';
$article_archived->save();
try {
$access_handler->resetCache();
$this->workspaces['stage']
->publish();
$this->fail('The expected exception was not thrown.');
} catch (WorkspacePublishException $e) {
$this->assertEquals('The Stage workspace can not be published because it contains 1 item in an unpublished moderation state.', $e->getMessage());
}
// Get the $article_draft node to a publishable state and try again.
$article_draft->moderation_state->value = 'published';
$article_draft->save();
$access_handler->resetCache();
$this->workspaces['stage']
->publish();
}
/**
* Publish a workspace with workflows including no tracked default revisions.
*/
public function testContentModerationWithoutDefaultRevisionsInWorkspaces() : void {
$access_handler = $this->container
->get('entity_type.manager')
->getAccessControlHandler('workspace');
// Create a workflow which has the same states as the 'editorial' one,
// but it doesn't create any default revisions. This covers the case when a
// workspace is published containing no tracked types. This has to be the
// only workflow.
$editorial = $this->createEditorialWorkflow();
$type_settings = $editorial->get('type_settings');
$type_settings['states']['draft']['default_revision'] = FALSE;
$type_settings['states']['archived']['default_revision'] = FALSE;
$this->workspaceManager
->executeOutsideWorkspace(function () use ($editorial) {
$editorial->save();
});
// Create an node bundle 'note' that uses non-default workflow.
$this->createContentType([
'type' => 'note',
]);
// Create content in all states none with default revisions.
$note_archived = Node::create([
'type' => 'note',
'title' => 'Test note - archived',
'moderation_state' => 'archived',
]);
$note_archived->save();
$note_draft = Node::create([
'type' => 'note',
'title' => 'Test note - draft',
'moderation_state' => 'draft',
]);
$note_draft->save();
$note_published = Node::create([
'type' => 'note',
'title' => 'Test note - published',
'moderation_state' => 'published',
]);
$note_published->save();
// Check workspace can be published.
$access_handler->resetCache();
$this->workspaces['stage']
->publish();
}
/**
* Publish a workspace with multiple entities from different entity types.
*/
public function testContentModerationMultipleEntityTypesWithWorkspaces() : void {
$editorial = $this->createEditorialWorkflow();
$this->createContentType([
'type' => 'page',
]);
$this->addEntityTypeAndBundleToWorkflow($editorial, 'node', 'page');
$this->addEntityTypeAndBundleToWorkflow($editorial, 'entity_test_mulrevpub', 'entity_test_mulrevpub');
// Create an entity with a previous revision that is tracked in unpublished
// state.
$entity_with_revision = EntityTestMulRevPub::create([
'title' => 'Test entity mulrevpub',
'type' => 'entity_test_mulrevpub',
'moderation_state' => 'draft',
]);
$entity_with_revision->save();
$entity_with_revision->save();
$entity_with_revision = $this->reloadEntity($entity_with_revision);
// Confirm unpublished earlier revision.
$this->assertEquals('draft', $entity_with_revision->moderation_state->value);
$earlier_revision_id = $entity_with_revision->getRevisionId();
// Publish.
$entity_with_revision->moderation_state->value = 'published';
$entity_with_revision->save();
$entity_with_revision = $this->reloadEntity($entity_with_revision);
// Confirm publish revision.
$this->assertEquals('published', $entity_with_revision->moderation_state->value);
$published_revision_id = $entity_with_revision->getRevisionId();
$this->assertNotEquals($earlier_revision_id, $published_revision_id);
// Create an entity that has a default revision id the same as the previous
// entity's old revision.
$entity_without_revision = Node::create([
'title' => 'Test node page',
'type' => 'page',
'moderation_state' => 'published',
]);
$entity_without_revision->save();
$entity_without_revision = $this->reloadEntity($entity_without_revision);
$this->assertEquals('published', $entity_without_revision->moderation_state->value);
// Current published revisions of second entity has the same revision as
// earlier unpublished revision of first entity.
$this->assertEquals($entity_without_revision->getRevisionId(), $earlier_revision_id);
$this->workspaces['stage']
->publish();
}
/**
* Test cases for basic moderation test.
*/
public static function basicModerationTestCases() {
return [
'Nodes' => [
'node',
],
'Block content' => [
'block_content',
],
'Media' => [
'media',
],
'Test entity - revisions, data table, and published interface' => [
'entity_test_mulrevpub',
],
'Entity Test with revisions and published status' => [
'entity_test_revpub',
],
];
}
/**
* {@inheritdoc}
*/
public function testContentModerationStateDataRemoval($entity_type_id = NULL) : void {
// Deliberately empty body. This test will be skipped in the setUp() of this
// class. However, for it to be picked up there, in PHPUnit 9, it still
// needs to be defined in this class.
}
/**
* {@inheritdoc}
*/
public function testModerationWithFieldConfigOverride() : void {
// Deliberately empty body. This test will be skipped in the setUp() of this
// class. However, for it to be picked up there, in PHPUnit 9, it still
// needs to be defined in this class.
}
/**
* {@inheritdoc}
*/
public function testWorkflowDependencies() : void {
// Deliberately empty body. This test will be skipped in the setUp() of this
// class. However, for it to be picked up there, in PHPUnit 9, it still
// needs to be defined in this class.
}
/**
* {@inheritdoc}
*/
public function testWorkflowNonConfigBundleDependencies() : void {
// Deliberately empty body. This test will be skipped in the setUp() of this
// class. However, for it to be picked up there, in PHPUnit 9, it still
// needs to be defined in this class.
}
/**
* {@inheritdoc}
*/
public function testGetCurrentUserId() : void {
// Deliberately empty body. This test will be skipped in the setUp() of this
// class. However, for it to be picked up there, in PHPUnit 9, it still
// needs to be defined in this class.
}
/**
* {@inheritdoc}
*/
protected function createEntity($entity_type_id, $moderation_state = 'published', $create_workflow = TRUE) {
$entity = $this->workspaceManager
->executeOutsideWorkspace(function () use ($entity_type_id, $moderation_state, $create_workflow) {
return parent::createEntity($entity_type_id, $moderation_state, $create_workflow);
});
return $entity;
}
/**
* {@inheritdoc}
*/
protected function createEditorialWorkflow() {
$workflow = $this->workspaceManager
->executeOutsideWorkspace(function () {
return $this->traitCreateEditorialWorkflow();
});
return $workflow;
}
/**
* {@inheritdoc}
*/
protected function addEntityTypeAndBundleToWorkflow(WorkflowInterface $workflow, $entity_type_id, $bundle) {
$this->workspaceManager
->executeOutsideWorkspace(function () use ($workflow, $entity_type_id, $bundle) {
$this->traitAddEntityTypeAndBundleToWorkflow($workflow, $entity_type_id, $bundle);
});
}
/**
* {@inheritdoc}
*/
protected function createContentType(array $values = []) {
$note_type = $this->workspaceManager
->executeOutsideWorkspace(function () use ($values) {
return $this->traitCreateContentType($values);
});
return $note_type;
}
/**
* {@inheritdoc}
*/
protected function assertDefaultRevision(EntityInterface $entity, int $revision_id, $published = TRUE) : void {
// In the context of a workspace, the default revision ID is always the
// latest workspace-specific revision, so we need to adjust the expectation
// of the parent assertion.
$revision_id = (int) $this->entityTypeManager
->getStorage($entity->getEntityTypeId())
->load($entity->id())
->getRevisionId();
// Additionally, the publishing status of the default revision is not
// relevant in a workspace, because getting an entity to a "published"
// moderation state doesn't automatically make it the default revision, so
// we have to disable that assertion.
$published = NULL;
parent::assertDefaultRevision($entity, $revision_id, $published);
}
}
Classes
Title | Deprecated | Summary |
---|---|---|
WorkspacesContentModerationStateTest | Tests that Workspaces and Content Moderation work together properly. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.