class ContentModerationStateTest

Same name and namespace in other branches
  1. 11.x core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php \Drupal\Tests\content_moderation\Kernel\ContentModerationStateTest
  2. 10 core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php \Drupal\Tests\content_moderation\Kernel\ContentModerationStateTest
  3. 9 core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php \Drupal\Tests\content_moderation\Kernel\ContentModerationStateTest
  4. 8.9.x core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php \Drupal\Tests\content_moderation\Kernel\ContentModerationStateTest

Tests links between a content entity and a content_moderation_state entity.

Attributes

#[Group('content_moderation')] #[RunTestsInSeparateProcesses]

Hierarchy

Expanded class hierarchy of ContentModerationStateTest

File

core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php, line 29

Namespace

Drupal\Tests\content_moderation\Kernel
View source
class ContentModerationStateTest extends KernelTestBase {
  use ContentModerationTestTrait;
  use EntityDefinitionTestTrait;
  use ContentTypeCreationTrait;
  
  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'entity_test',
    'node',
    'block',
    'block_content',
    'media',
    'media_test_source',
    'image',
    'file',
    'field',
    'filter',
    'content_moderation',
    'user',
    'system',
    'language',
    'text',
    'workflows',
    'taxonomy',
  ];
  
  /**
   * @var \Drupal\Core\Entity\EntityTypeManager
   */
  protected $entityTypeManager;
  
  /**
   * The state object.
   *
   * @var \Drupal\Core\State\StateInterface
   */
  protected StateInterface $state;
  
  /**
   * The entity definition update manager.
   *
   * @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
   */
  protected EntityDefinitionUpdateManagerInterface $entityDefinitionUpdateManager;
  
  /**
   * The ID of the revisionable entity type used in the tests.
   *
   * @var string
   */
  protected $revEntityTypeId = 'entity_test_rev';
  
  /**
   * {@inheritdoc}
   */
  protected function setUp() : void {
    parent::setUp();
    $this->installConfig('system');
    $this->installSchema('node', 'node_access');
    $this->installEntitySchema('node');
    $this->installEntitySchema('user');
    $this->installEntitySchema($this->revEntityTypeId);
    $this->installEntitySchema('entity_test_no_bundle');
    $this->installEntitySchema('entity_test_mulrevpub');
    $this->installEntitySchema('block_content');
    $this->installEntitySchema('media');
    $this->installEntitySchema('file');
    $this->installEntitySchema('taxonomy_term');
    $this->installEntitySchema('content_moderation_state');
    $this->installConfig('content_moderation');
    $this->installSchema('file', 'file_usage');
    $this->installConfig([
      'field',
      'file',
      'filter',
      'image',
      'media',
      'node',
    ]);
    // Add the French language.
    ConfigurableLanguage::createFromLangcode('fr')->save();
    $this->entityTypeManager = $this->container
      ->get('entity_type.manager');
  }
  
  /**
   * Tests basic monolingual content moderation through the API.
   */
  public function testBasicModeration() : void {
    foreach (static::basicModerationTestCases() as $case) {
      [$entity_type_id] = $case;
      $this->doTestBasicModeration($entity_type_id);
    }
  }
  
  /**
   * Tests basic monolingual content moderation through the API.
   */
  protected function doTestBasicModeration($entity_type_id) : void {
    $entity = $this->createEntity($entity_type_id, 'draft');
    $entity = $this->reloadEntity($entity);
    $this->assertEquals('draft', $entity->moderation_state->value);
    $entity->moderation_state->value = 'published';
    $entity->save();
    $entity = $this->reloadEntity($entity);
    $this->assertEquals('published', $entity->moderation_state->value);
    // Change the state without saving the node.
    $content_moderation_state = ContentModerationState::load(1);
    $content_moderation_state->set('moderation_state', 'draft');
    $content_moderation_state->setNewRevision(TRUE);
    $content_moderation_state->save();
    $entity = $this->reloadEntity($entity, 3);
    $this->assertEquals('draft', $entity->moderation_state->value);
    if ($entity instanceof EntityPublishedInterface) {
      $this->assertFalse($entity->isPublished());
    }
    // Check the default revision.
    $this->assertDefaultRevision($entity, 2);
    $entity->moderation_state->value = 'published';
    $entity->save();
    $entity = $this->reloadEntity($entity, 4);
    $this->assertEquals('published', $entity->moderation_state->value);
    // Check the default revision.
    $this->assertDefaultRevision($entity, 4);
    // Update the node to archived which will then be the default revision.
    $entity->moderation_state->value = 'archived';
    $entity->save();
    // Revert to the previous (published) revision.
    /** @var \Drupal\Core\Entity\RevisionableStorageInterface $entity_storage */
    $entity_storage = $this->entityTypeManager
      ->getStorage($entity_type_id);
    $previous_revision = $entity_storage->loadRevision(4);
    $previous_revision->isDefaultRevision(TRUE);
    $previous_revision->setNewRevision(TRUE);
    $previous_revision->save();
    // Check the default revision.
    $this->assertDefaultRevision($entity, 6);
    // Set an invalid moderation state.
    $this->expectException(EntityStorageException::class);
    $entity->moderation_state->value = 'foobar';
    $entity->save();
  }
  
  /**
   * Test cases for basic moderation test.
   */
  public static function basicModerationTestCases() : array {
    return [
      'Nodes' => [
        'node',
      ],
      'Taxonomy term' => [
        'taxonomy_term',
      ],
      'Block content' => [
        'block_content',
      ],
      'Media' => [
        'media',
      ],
      'Test entity - revisions, data table, and published interface' => [
        'entity_test_mulrevpub',
      ],
      'Entity Test with revisions' => [
        'entity_test_rev',
      ],
      'Entity without bundle' => [
        'entity_test_no_bundle',
      ],
    ];
  }
  
  /**
   * Tests removal of content moderation state entity.
   */
  public function testContentModerationStateDataRemoval() : void {
    foreach (static::basicModerationTestCases() as $case) {
      [$entity_type_id] = $case;
      $this->doTestContentModerationStateDataRemoval($entity_type_id);
    }
  }
  
  /**
   * Tests removal of content moderation state entity.
   */
  public function doTestContentModerationStateDataRemoval($entity_type_id) : void {
    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
    $entity = $this->createEntity($entity_type_id);
    $entity = $this->reloadEntity($entity);
    $entity->delete();
    $content_moderation_state = ContentModerationState::loadFromModeratedEntity($entity);
    $this->assertNull($content_moderation_state);
  }
  
  /**
   * Tests removal of content moderation state entity revisions.
   */
  public function testContentModerationStateRevisionDataRemoval() : void {
    foreach (static::basicModerationTestCases() as $case) {
      [$entity_type_id] = $case;
      $this->doTestContentModerationStateRevisionDataRemoval($entity_type_id);
    }
  }
  
  /**
   * Tests removal of content moderation state entity revisions.
   */
  public function doTestContentModerationStateRevisionDataRemoval($entity_type_id) : void {
    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
    $entity = $this->createEntity($entity_type_id);
    $revision_1 = clone $entity;
    $this->assertNotNull(ContentModerationState::loadFromModeratedEntity($revision_1));
    // Create a second revision.
    $entity = $this->reloadEntity($entity);
    $entity->setNewRevision(TRUE);
    $entity->save();
    $revision_2 = clone $entity;
    // Create a third revision.
    $entity = $this->reloadEntity($entity);
    $entity->setNewRevision(TRUE);
    $entity->save();
    $revision_3 = clone $entity;
    // Delete the second revision and check that its content moderation state is
    // removed as well, while the content moderation states for revisions 1 and
    // 3 are kept in place.
    /** @var \Drupal\Core\Entity\RevisionableStorageInterface $entity_storage */
    $entity_storage = $this->entityTypeManager
      ->getStorage($entity_type_id);
    $entity_storage->deleteRevision($revision_2->getRevisionId());
    $this->assertNotNull(ContentModerationState::loadFromModeratedEntity($revision_1));
    $this->assertNull(ContentModerationState::loadFromModeratedEntity($revision_2));
    $this->assertNotNull(ContentModerationState::loadFromModeratedEntity($revision_3));
  }
  
  /**
   * Tests removal of content moderation state pending entity revisions.
   */
  public function testContentModerationStatePendingRevisionDataRemoval() : void {
    foreach (static::basicModerationTestCases() as $case) {
      [$entity_type_id] = $case;
      $this->doTestContentModerationStatePendingRevisionDataRemoval($entity_type_id);
    }
  }
  
  /**
   * Tests removal of content moderation state pending entity revisions.
   */
  public function doTestContentModerationStatePendingRevisionDataRemoval($entity_type_id) : void {
    $entity = $this->createEntity($entity_type_id, 'published');
    $entity->setNewRevision(TRUE);
    $entity->moderation_state = 'draft';
    $entity->save();
    $content_moderation_state = ContentModerationState::loadFromModeratedEntity($entity);
    $this->assertNotEmpty($content_moderation_state);
    /** @var \Drupal\Core\Entity\RevisionableStorageInterface $entity_storage */
    $entity_storage = $this->entityTypeManager
      ->getStorage($entity_type_id);
    $entity_storage->deleteRevision($entity->getRevisionId());
    $content_moderation_state = ContentModerationState::loadFromModeratedEntity($entity);
    $this->assertNull($content_moderation_state);
  }
  
  /**
   * Tests removal of content moderation state entities for preexisting content.
   */
  public function testExistingContentModerationStateDataRemoval() : void {
    /** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */
    $storage = $this->entityTypeManager
      ->getStorage('entity_test_mulrevpub');
    $entity = $this->createEntity('entity_test_mulrevpub', 'published', FALSE);
    $original_revision_id = $entity->getRevisionId();
    $workflow = $this->createEditorialWorkflow();
    $this->addEntityTypeAndBundleToWorkflow($workflow, $entity->getEntityTypeId(), $entity->bundle());
    $entity = $this->reloadEntity($entity);
    $entity->moderation_state = 'draft';
    $entity->save();
    $storage->deleteRevision($entity->getRevisionId());
    $entity = $this->reloadEntity($entity);
    $this->assertEquals('published', $entity->moderation_state->value);
    $this->assertEquals($original_revision_id, $storage->getLatestRevisionId($entity->id()));
  }
  
  /**
   * Tests removal of content moderation state translations.
   */
  public function testContentModerationStateTranslationDataRemoval() : void {
    foreach (static::basicModerationTestCases() as $case) {
      [$entity_type_id] = $case;
      $this->doTestContentModerationStateTranslationDataRemoval($entity_type_id);
    }
  }
  
  /**
   * Tests removal of content moderation state translations.
   */
  public function doTestContentModerationStateTranslationDataRemoval($entity_type_id) : void {
    // Test content moderation state translation deletion.
    if ($this->entityTypeManager
      ->getDefinition($entity_type_id)
      ->isTranslatable()) {
      /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
      $entity = $this->createEntity($entity_type_id, 'published');
      $langcode = 'fr';
      $translation = $entity->addTranslation($langcode, [
        $entity->getEntityType()
          ->getKey('label') => 'French title test',
      ]);
      // Make sure we add values for all of the required fields.
      if ($entity_type_id == 'block_content') {
        $translation->info = $this->randomString();
      }
      $translation->save();
      $content_moderation_state = ContentModerationState::loadFromModeratedEntity($entity);
      $this->assertTrue($content_moderation_state->hasTranslation($langcode));
      $entity->removeTranslation($langcode);
      $entity->save();
      $content_moderation_state = ContentModerationState::loadFromModeratedEntity($entity);
      $this->assertFalse($content_moderation_state->hasTranslation($langcode));
    }
  }
  
  /**
   * Tests basic multilingual content moderation through the API.
   */
  public function testMultilingualModeration() : void {
    $this->createContentType([
      'type' => 'example',
    ]);
    $workflow = $this->createEditorialWorkflow();
    $this->addEntityTypeAndBundleToWorkflow($workflow, 'node', 'example');
    $english_node = Node::create([
      'type' => 'example',
      'title' => 'Test title',
    ]);
    // Revision 1 (en).
    $english_node->setUnpublished()
      ->save();
    $this->assertEquals('draft', $english_node->moderation_state->value);
    $this->assertFalse($english_node->isPublished());
    // Create a French translation.
    $french_node = $english_node->addTranslation('fr', [
      'title' => 'French title',
    ]);
    $french_node->setUnpublished();
    // Revision 2 (fr).
    $french_node->save();
    $french_node = $this->reloadEntity($english_node)
      ->getTranslation('fr');
    $this->assertEquals('draft', $french_node->moderation_state->value);
    $this->assertFalse($french_node->isPublished());
    // Move English node to create another draft.
    $english_node = $this->reloadEntity($english_node);
    $english_node->moderation_state->value = 'draft';
    // Revision 3 (en, fr).
    $english_node->save();
    $english_node = $this->reloadEntity($english_node);
    $this->assertEquals('draft', $english_node->moderation_state->value);
    // French node should still be in draft.
    $french_node = $this->reloadEntity($english_node)
      ->getTranslation('fr');
    $this->assertEquals('draft', $french_node->moderation_state->value);
    // Publish the French node.
    $french_node->moderation_state->value = 'published';
    // Revision 4 (en, fr).
    $french_node->save();
    $french_node = $this->reloadEntity($french_node)
      ->getTranslation('fr');
    $this->assertTrue($french_node->isPublished());
    $this->assertEquals('published', $french_node->moderation_state->value);
    $this->assertTrue($french_node->isPublished());
    $english_node = $french_node->getTranslation('en');
    $this->assertEquals('draft', $english_node->moderation_state->value);
    // Publish the English node.
    $english_node->moderation_state->value = 'published';
    // Revision 5 (en, fr).
    $english_node->save();
    $english_node = $this->reloadEntity($english_node);
    $this->assertTrue($english_node->isPublished());
    // Move the French node back to draft.
    $french_node = $this->reloadEntity($english_node)
      ->getTranslation('fr');
    $this->assertTrue($french_node->isPublished());
    $french_node->moderation_state->value = 'draft';
    // Revision 6 (en, fr).
    $french_node->save();
    $french_node = $this->reloadEntity($english_node, 6)
      ->getTranslation('fr');
    $this->assertFalse($french_node->isPublished());
    $this->assertTrue($french_node->getTranslation('en')
      ->isPublished());
    // Republish the French node.
    $french_node->moderation_state->value = 'published';
    // Revision 7 (en, fr).
    $french_node->save();
    $french_node = $this->reloadEntity($english_node)
      ->getTranslation('fr');
    $this->assertTrue($french_node->isPublished());
    // Change the EN state without saving the node.
    $content_moderation_state = ContentModerationState::loadFromModeratedEntity($english_node);
    $content_moderation_state->set('moderation_state', 'draft');
    $content_moderation_state->setNewRevision(TRUE);
    // Revision 8 (en, fr).
    $content_moderation_state->save();
    $english_node = $this->reloadEntity($french_node, $french_node->getRevisionId() + 1);
    $this->assertEquals('draft', $english_node->moderation_state->value);
    $french_node = $this->reloadEntity($english_node)
      ->getTranslation('fr');
    $this->assertEquals('published', $french_node->moderation_state->value);
    // This should unpublish the French node.
    $content_moderation_state = ContentModerationState::loadFromModeratedEntity($english_node);
    $content_moderation_state = $content_moderation_state->getTranslation('fr');
    $content_moderation_state->set('moderation_state', 'draft');
    $content_moderation_state->setNewRevision(TRUE);
    // Revision 9 (en, fr).
    $content_moderation_state->save();
    $english_node = $this->reloadEntity($english_node, $english_node->getRevisionId());
    $this->assertEquals('draft', $english_node->moderation_state->value);
    $french_node = $this->reloadEntity($english_node, '9')
      ->getTranslation('fr');
    $this->assertEquals('draft', $french_node->moderation_state->value);
    // Switching the moderation state to an unpublished state should update the
    // entity.
    $this->assertFalse($french_node->isPublished());
    // Check that revision 7 is still the default one for the node.
    $this->assertDefaultRevision($english_node, 7);
  }
  
  /**
   * Tests moderation when the moderation_state field has a config override.
   */
  public function testModerationWithFieldConfigOverride() : void {
    $this->createContentType([
      'type' => 'test_type',
    ]);
    $workflow = $this->createEditorialWorkflow();
    $this->addEntityTypeAndBundleToWorkflow($workflow, 'node', 'test_type');
    $fields = $this->container
      ->get('entity_field.manager')
      ->getFieldDefinitions('node', 'test_type');
    $field_config = $fields['moderation_state']->getConfig('test_type');
    $field_config->setLabel('Field Override!');
    $field_config->save();
    $node = Node::create([
      'title' => 'Test node',
      'type' => 'test_type',
    ]);
    $node->save();
    $this->assertFalse($node->isPublished());
    $this->assertEquals('draft', $node->moderation_state->value);
    $node->moderation_state = 'published';
    $node->save();
    $this->assertTrue($node->isPublished());
    $this->assertEquals('published', $node->moderation_state->value);
  }
  
  /**
   * Tests that entities with special languages can be moderated.
   */
  public function testModerationWithSpecialLanguages(string $original_language, string $updated_language) : void {
    $workflow = $this->createEditorialWorkflow();
    $this->addEntityTypeAndBundleToWorkflow($workflow, $this->revEntityTypeId, $this->revEntityTypeId);
    // Create a test entity.
    $storage = $this->entityTypeManager
      ->getStorage($this->revEntityTypeId);
    $entity = $storage->create([
      'langcode' => $original_language,
    ]);
    $entity->save();
    $this->assertEquals('draft', $entity->moderation_state->value);
    $entity->moderation_state->value = 'published';
    $entity->langcode = $updated_language;
    $entity->save();
    $this->assertEquals('published', $storage->load($entity->id())->moderation_state->value);
  }
  
  /**
   * Test cases for ::testModerationWithSpecialLanguages().
   */
  public static function moderationWithSpecialLanguagesTestCases() : array {
    return [
      'Not specified to not specified' => [
        LanguageInterface::LANGCODE_NOT_SPECIFIED,
        LanguageInterface::LANGCODE_NOT_SPECIFIED,
      ],
      'English to not specified' => [
        'en',
        LanguageInterface::LANGCODE_NOT_SPECIFIED,
      ],
      'Not specified to english' => [
        LanguageInterface::LANGCODE_NOT_SPECIFIED,
        'en',
      ],
    ];
  }
  
  /**
   * Tests changing the language of content without adding a translation.
   */
  public function testChangingContentLangcode() : void {
    $this->createContentType([
      'type' => 'test_type',
    ]);
    $workflow = $this->createEditorialWorkflow();
    $this->addEntityTypeAndBundleToWorkflow($workflow, 'node', 'test_type');
    $entity = Node::create([
      'title' => 'Test node',
      'langcode' => 'en',
      'type' => 'test_type',
    ]);
    $entity->save();
    $content_moderation_state = ContentModerationState::loadFromModeratedEntity($entity);
    $this->assertCount(1, $entity->getTranslationLanguages());
    $this->assertCount(1, $content_moderation_state->getTranslationLanguages());
    $this->assertEquals('en', $entity->langcode->value);
    $this->assertEquals('en', $content_moderation_state->langcode->value);
    $entity->langcode = 'fr';
    $entity->save();
    $content_moderation_state = ContentModerationState::loadFromModeratedEntity($entity);
    $this->assertCount(1, $entity->getTranslationLanguages());
    $this->assertCount(1, $content_moderation_state->getTranslationLanguages());
    $this->assertEquals('fr', $entity->langcode->value);
    $this->assertEquals('fr', $content_moderation_state->langcode->value);
  }
  
  /**
   * Tests that a non-translatable entity type with a langcode can be moderated.
   */
  public function testNonTranslatableEntityTypeModeration() : void {
    $workflow = $this->createEditorialWorkflow();
    $this->addEntityTypeAndBundleToWorkflow($workflow, $this->revEntityTypeId, $this->revEntityTypeId);
    // Check that the tested entity type is not translatable.
    $entity_type = $this->entityTypeManager
      ->getDefinition($this->revEntityTypeId);
    $this->assertFalse($entity_type->isTranslatable(), 'The test entity type is not translatable.');
    // Create a test entity.
    $storage = $this->entityTypeManager
      ->getStorage($this->revEntityTypeId);
    $entity = $storage->create();
    $entity->save();
    $this->assertEquals('draft', $entity->moderation_state->value);
    $entity->moderation_state->value = 'published';
    $entity->save();
    $this->assertEquals('published', $storage->load($entity->id())->moderation_state->value);
  }
  
  /**
   * Tests moderation of a non-translatable entity type with no langcode.
   */
  public function testNonLangcodeEntityTypeModeration() : void {
    // Unset the langcode entity key for 'entity_test_rev'.
    $entity_type = clone $this->entityTypeManager
      ->getDefinition($this->revEntityTypeId);
    $keys = $entity_type->getKeys();
    unset($keys['langcode']);
    $entity_type->set('entity_keys', $keys);
    \Drupal::state()->set($this->revEntityTypeId . '.entity_type', $entity_type);
    // Update the entity type in order to remove the 'langcode' field.
    \Drupal::entityDefinitionUpdateManager()->updateFieldableEntityType($entity_type, \Drupal::service('entity_field.manager')->getFieldStorageDefinitions($entity_type->id()));
    $workflow = $this->createEditorialWorkflow();
    $this->addEntityTypeAndBundleToWorkflow($workflow, $this->revEntityTypeId, $this->revEntityTypeId);
    // Check that the tested entity type is not translatable and does not have a
    // 'langcode' entity key.
    $entity_type = $this->entityTypeManager
      ->getDefinition($this->revEntityTypeId);
    $this->assertFalse($entity_type->isTranslatable(), 'The test entity type is not translatable.');
    $this->assertFalse($entity_type->getKey('langcode'), "The test entity type does not have a 'langcode' entity key.");
    // Create a test entity.
    $storage = $this->entityTypeManager
      ->getStorage($this->revEntityTypeId);
    $entity = $storage->create();
    $entity->save();
    $this->assertEquals('draft', $entity->moderation_state->value);
    $entity->moderation_state->value = 'published';
    $entity->save();
    $this->assertEquals('published', $storage->load($entity->id())->moderation_state->value);
  }
  
  /**
   * Tests the dependencies of the workflow when using content moderation.
   */
  public function testWorkflowDependencies() : void {
    $node_type = $this->createContentType([
      'type' => 'example',
    ]);
    $workflow = $this->createEditorialWorkflow();
    // Test both a config and non-config based bundle and entity type.
    $this->addEntityTypeAndBundleToWorkflow($workflow, 'node', 'example');
    $this->addEntityTypeAndBundleToWorkflow($workflow, 'entity_test_rev', 'entity_test_rev');
    $this->addEntityTypeAndBundleToWorkflow($workflow, 'entity_test_no_bundle', 'entity_test_no_bundle');
    $this->assertEquals([
      'module' => [
        'content_moderation',
        'entity_test',
      ],
      'config' => [
        'node.type.example',
      ],
    ], $workflow->getDependencies());
    $this->assertEquals([
      'entity_test_no_bundle',
      'entity_test_rev',
      'node',
    ], $workflow->getTypePlugin()
      ->getEntityTypes());
    // Delete the node type and ensure it is removed from the workflow.
    $node_type->delete();
    $workflow = Workflow::load('editorial');
    $entity_types = $workflow->getTypePlugin()
      ->getEntityTypes();
    $this->assertNotContains('node', $entity_types);
    // Uninstall entity test and ensure it's removed from the workflow.
    $this->container
      ->get('config.manager')
      ->uninstall('module', 'entity_test');
    $workflow = Workflow::load('editorial');
    $entity_types = $workflow->getTypePlugin()
      ->getEntityTypes();
    $this->assertEquals([], $entity_types);
  }
  
  /**
   * Tests the content moderation workflow dependencies for non-config bundles.
   */
  public function testWorkflowNonConfigBundleDependencies() : void {
    // Create a bundle not based on any particular configuration.
    EntityTestHelper::createBundle('test_bundle');
    $workflow = $this->createEditorialWorkflow();
    $workflow->getTypePlugin()
      ->addEntityTypeAndBundle('entity_test', 'test_bundle');
    $workflow->save();
    // Ensure the bundle is correctly added to the workflow.
    $this->assertEquals([
      'module' => [
        'content_moderation',
        'entity_test',
      ],
    ], $workflow->getDependencies());
    $this->assertEquals([
      'test_bundle',
    ], $workflow->getTypePlugin()
      ->getBundlesForEntityType('entity_test'));
    // Delete the test bundle to ensure the workflow entity responds
    // appropriately.
    EntityTestHelper::deleteBundle('test_bundle');
    $workflow = Workflow::load('editorial');
    $this->assertEquals([], $workflow->getTypePlugin()
      ->getBundlesForEntityType('entity_test'));
    $this->assertEquals([
      'module' => [
        'content_moderation',
      ],
    ], $workflow->getDependencies());
  }
  
  /**
   * Tests the revision default state of the moderation state entity revisions.
   */
  public function testRevisionDefaultState() : void {
    foreach (static::basicModerationTestCases() as $case) {
      [$entity_type_id] = $case;
      $this->doTestRevisionDefaultState($entity_type_id);
    }
  }
  
  /**
   * Tests the revision default state of the moderation state entity revisions.
   *
   * @param string $entity_type_id
   *   The ID of entity type to be tested.
   */
  public function doTestRevisionDefaultState($entity_type_id) : void {
    // Check that the revision default state of the moderated entity and the
    // content moderation state entity always match.
    $entity = $this->createEntity($entity_type_id, 'published');
    $cms_entity = ContentModerationState::loadFromModeratedEntity($entity);
    $this->assertEquals($entity->isDefaultRevision(), $cms_entity->isDefaultRevision());
    $entity->get('moderation_state')->value = 'published';
    $entity->save();
    $cms_entity = ContentModerationState::loadFromModeratedEntity($entity);
    $this->assertEquals($entity->isDefaultRevision(), $cms_entity->isDefaultRevision());
    $entity->get('moderation_state')->value = 'draft';
    $entity->save();
    $cms_entity = ContentModerationState::loadFromModeratedEntity($entity);
    $this->assertEquals($entity->isDefaultRevision(), $cms_entity->isDefaultRevision());
    $entity->get('moderation_state')->value = 'published';
    $entity->save();
    $cms_entity = ContentModerationState::loadFromModeratedEntity($entity);
    $this->assertEquals($entity->isDefaultRevision(), $cms_entity->isDefaultRevision());
  }
  
  /**
   * Creates an entity.
   *
   * The entity will have required fields populated and the corresponding bundle
   * will be enabled for content moderation.
   *
   * @param string $entity_type_id
   *   The entity type ID.
   * @param string $moderation_state
   *   (optional) The initial moderation state of the newly created entity.
   *   Defaults to 'published'.
   * @param bool $create_workflow
   *   (optional) Whether to create an editorial workflow and configure it for
   *   the given entity type. Defaults to TRUE.
   *
   * @return \Drupal\Core\Entity\ContentEntityInterface
   *   The created entity.
   */
  protected function createEntity($entity_type_id, $moderation_state = 'published', $create_workflow = TRUE) {
    $entity_type = $this->entityTypeManager
      ->getDefinition($entity_type_id);
    $bundle_id = $entity_type_id;
    // Set up a bundle entity type for the specified entity type, if needed.
    if ($bundle_entity_type_id = $entity_type->getBundleEntityType()) {
      $bundle_entity_type = $this->entityTypeManager
        ->getDefinition($bundle_entity_type_id);
      $bundle_entity_storage = $this->entityTypeManager
        ->getStorage($bundle_entity_type_id);
      $bundle_id = 'example';
      if (!$bundle_entity_storage->load($bundle_id)) {
        $bundle_entity = $bundle_entity_storage->create([
          $bundle_entity_type->getKey('id') => 'example',
        ]);
        if ($bundle_entity_type->hasKey('label')) {
          $bundle_entity->set($bundle_entity_type->getKey('label'), $this->randomMachineName());
        }
        if ($entity_type_id == 'media') {
          $bundle_entity->set('source', 'test');
          $bundle_entity->save();
          $source_field = $bundle_entity->getSource()
            ->createSourceField($bundle_entity);
          $source_field->getFieldStorageDefinition()
            ->save();
          $source_field->save();
          $bundle_entity->set('source_configuration', [
            'source_field' => $source_field->getName(),
          ]);
        }
        $bundle_entity->save();
      }
    }
    if ($create_workflow) {
      $workflow = $this->createEditorialWorkflow();
      $workflow->getTypePlugin()
        ->addEntityTypeAndBundle($entity_type_id, $bundle_id);
      $workflow->save();
    }
    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
    $entity_storage = $this->entityTypeManager
      ->getStorage($entity_type_id);
    $entity = $entity_storage->create([
      $entity_type->getKey('label') => 'Test title',
      $entity_type->getKey('bundle') => $bundle_id,
      'moderation_state' => $moderation_state,
    ]);
    // Make sure we add values for all of the required fields.
    if ($entity_type_id == 'block_content') {
      $entity->info = $this->randomString();
    }
    $entity->save();
    return $entity;
  }
  
  /**
   * Reloads the entity after clearing the static cache.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity to reload.
   * @param int|bool $revision_id
   *   The specific revision ID to load. Defaults FALSE and just loads the
   *   default revision.
   *
   * @return \Drupal\Core\Entity\EntityInterface
   *   The reloaded entity.
   */
  protected function reloadEntity(EntityInterface $entity, $revision_id = FALSE) : EntityInterface {
    /** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */
    $storage = \Drupal::entityTypeManager()->getStorage($entity->getEntityTypeId());
    $storage->resetCache([
      $entity->id(),
    ]);
    if ($revision_id) {
      return $storage->loadRevision($revision_id);
    }
    return $storage->load($entity->id());
  }
  
  /**
   * Checks the default revision ID and publishing status for an entity.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   An entity object.
   * @param int $revision_id
   *   The expected revision ID.
   * @param bool|null $published
   *   (optional) Whether to check if the entity is published or not. Defaults
   *   to TRUE.
   *
   * @internal
   */
  protected function assertDefaultRevision(EntityInterface $entity, int $revision_id, $published = TRUE) : void {
    // Get the default revision.
    $entity = $this->reloadEntity($entity);
    $this->assertEquals($revision_id, $entity->getRevisionId());
    if ($published !== NULL && $entity instanceof EntityPublishedInterface) {
      $this->assertSame($published, $entity->isPublished());
    }
  }

}

Members

Title Sort descending Deprecated Modifiers Object type Summary Overriden Title Overrides
AssertContentTrait::$content protected property The current raw content.
AssertContentTrait::$drupalSettings protected property The drupalSettings value from the current raw $content.
AssertContentTrait::$elements protected property The XML structure parsed from the current raw $content.
AssertContentTrait::$plainTextContent protected property The plain-text content of raw $content (text nodes).
AssertContentTrait::assertEscaped protected function Passes if the raw text IS found escaped on the loaded page, fail otherwise.
AssertContentTrait::assertField protected function Asserts that a field exists with the given name or ID.
AssertContentTrait::assertFieldByName protected function Asserts that a field exists with the given name and value.
AssertContentTrait::assertFieldByXPath protected function Asserts that a field exists in the current page by the given XPath.
AssertContentTrait::assertFieldsByValue protected function Asserts that a field exists in the current page with a given Xpath result.
AssertContentTrait::assertLink protected function Passes if a link with the specified label is found.
AssertContentTrait::assertLinkByHref protected function Passes if a link containing a given href (part) is found.
AssertContentTrait::assertNoLink protected function Passes if a link with the specified label is not found.
AssertContentTrait::assertNoPattern protected function Triggers a pass if the perl regex pattern is not found in raw content.
AssertContentTrait::assertNoRaw protected function Passes if the raw text is NOT found on the loaded page, fail otherwise.
AssertContentTrait::assertNoText protected function Passes if the page (with HTML stripped) does not contains the text.
AssertContentTrait::assertPattern protected function Triggers a pass if the Perl regex pattern is found in the raw content.
AssertContentTrait::assertRaw protected function Passes if the raw text IS found on the loaded page, fail otherwise.
AssertContentTrait::assertText protected function Passes if the page (with HTML stripped) contains the text.
AssertContentTrait::assertTextHelper protected function Helper for assertText and assertNoText.
AssertContentTrait::assertThemeOutput protected function Asserts themed output.
AssertContentTrait::assertTitle protected function Pass if the page title is the given string.
AssertContentTrait::buildXPathQuery protected function Builds an XPath query.
AssertContentTrait::constructFieldXpath protected function Helper: Constructs an XPath for the given set of attributes and value.
AssertContentTrait::cssSelect protected function Searches elements using a CSS selector in the raw content.
AssertContentTrait::getAllOptions protected function Get all option elements, including nested options, in a select.
AssertContentTrait::getDrupalSettings protected function Gets the value of drupalSettings for the currently-loaded page.
AssertContentTrait::getRawContent protected function Gets the current raw content.
AssertContentTrait::getSelectedItem protected function Get the selected value from a select field.
AssertContentTrait::getTextContent protected function Retrieves the plain-text content from the current raw content.
AssertContentTrait::parse protected function Parse content returned from curlExec using DOM and SimpleXML.
AssertContentTrait::removeWhiteSpace protected function Removes all white-space between HTML tags from the raw content.
AssertContentTrait::setDrupalSettings protected function Sets the value of drupalSettings for the currently-loaded page.
AssertContentTrait::setRawContent protected function Sets the raw content (e.g. HTML).
AssertContentTrait::xpath protected function Performs an xpath search on the contents of the internal browser.
BodyFieldCreationTrait::createBodyField protected function Creates a field of an body field storage on the specified bundle.
BrowserHtmlDebugTrait::$htmlOutputBaseUrl protected property The Base URI to use for links to the output files.
BrowserHtmlDebugTrait::$htmlOutputClassName protected property Class name for HTML output logging.
BrowserHtmlDebugTrait::$htmlOutputCounter protected property Counter for HTML output logging.
BrowserHtmlDebugTrait::$htmlOutputCounterStorage protected property Counter storage for HTML output logging.
BrowserHtmlDebugTrait::$htmlOutputDirectory protected property Directory name for HTML output logging.
BrowserHtmlDebugTrait::$htmlOutputEnabled protected property HTML output enabled.
BrowserHtmlDebugTrait::$htmlOutputTestId protected property HTML output test ID.
BrowserHtmlDebugTrait::formatHtmlOutputHeaders protected function Formats HTTP headers as string for HTML output logging.
BrowserHtmlDebugTrait::getHtmlOutputHeaders protected function Returns headers in HTML output format. 1
BrowserHtmlDebugTrait::getResponseLogHandler protected function Provides a Guzzle middleware handler to log every response received.
BrowserHtmlDebugTrait::getTestMethodCaller protected function Retrieves the current calling line in the class under test. 1
BrowserHtmlDebugTrait::htmlOutput protected function Logs a HTML output message in a text file.
BrowserHtmlDebugTrait::initBrowserOutputFile protected function Creates the directory to store browser output.
ConfigTestTrait::configImporter protected function Returns a ConfigImporter object to import test configuration.
ConfigTestTrait::copyConfig protected function Copies configuration objects from source storage to target storage.
ContentModerationStateTest::$entityDefinitionUpdateManager protected property The entity definition update manager.
ContentModerationStateTest::$entityTypeManager protected property
ContentModerationStateTest::$modules protected static property Overrides KernelTestBase::$modules
ContentModerationStateTest::$revEntityTypeId protected property The ID of the revisionable entity type used in the tests. 1
ContentModerationStateTest::$state protected property The state object.
ContentModerationStateTest::assertDefaultRevision protected function Checks the default revision ID and publishing status for an entity. 1
ContentModerationStateTest::basicModerationTestCases public static function Test cases for basic moderation test. 1
ContentModerationStateTest::createEntity protected function Creates an entity.
ContentModerationStateTest::doTestBasicModeration protected function Tests basic monolingual content moderation through the API.
ContentModerationStateTest::doTestContentModerationStateDataRemoval public function Tests removal of content moderation state entity.
ContentModerationStateTest::doTestContentModerationStatePendingRevisionDataRemoval public function Tests removal of content moderation state pending entity revisions.
ContentModerationStateTest::doTestContentModerationStateRevisionDataRemoval public function Tests removal of content moderation state entity revisions.
ContentModerationStateTest::doTestContentModerationStateTranslationDataRemoval public function Tests removal of content moderation state translations.
ContentModerationStateTest::doTestRevisionDefaultState public function Tests the revision default state of the moderation state entity revisions.
ContentModerationStateTest::moderationWithSpecialLanguagesTestCases public static function Test cases for ::testModerationWithSpecialLanguages().
ContentModerationStateTest::reloadEntity protected function Reloads the entity after clearing the static cache.
ContentModerationStateTest::setUp protected function Overrides KernelTestBase::setUp 1
ContentModerationStateTest::testBasicModeration public function Tests basic monolingual content moderation through the API.
ContentModerationStateTest::testChangingContentLangcode public function Tests changing the language of content without adding a translation.
ContentModerationStateTest::testContentModerationStateDataRemoval public function Tests removal of content moderation state entity.
ContentModerationStateTest::testContentModerationStatePendingRevisionDataRemoval public function Tests removal of content moderation state pending entity revisions.
ContentModerationStateTest::testContentModerationStateRevisionDataRemoval public function Tests removal of content moderation state entity revisions.
ContentModerationStateTest::testContentModerationStateTranslationDataRemoval public function Tests removal of content moderation state translations.
ContentModerationStateTest::testExistingContentModerationStateDataRemoval public function Tests removal of content moderation state entities for preexisting content.
ContentModerationStateTest::testModerationWithFieldConfigOverride public function Tests moderation when the moderation_state field has a config override.
ContentModerationStateTest::testModerationWithSpecialLanguages public function Tests that entities with special languages can be moderated.
ContentModerationStateTest::testMultilingualModeration public function Tests basic multilingual content moderation through the API.
ContentModerationStateTest::testNonLangcodeEntityTypeModeration public function Tests moderation of a non-translatable entity type with no langcode.
ContentModerationStateTest::testNonTranslatableEntityTypeModeration public function Tests that a non-translatable entity type with a langcode can be moderated.
ContentModerationStateTest::testRevisionDefaultState public function Tests the revision default state of the moderation state entity revisions.
ContentModerationStateTest::testWorkflowDependencies public function Tests the dependencies of the workflow when using content moderation.
ContentModerationStateTest::testWorkflowNonConfigBundleDependencies public function Tests the content moderation workflow dependencies for non-config bundles.
ContentModerationTestTrait::addEntityTypeAndBundleToWorkflow protected function Adds an entity type ID / bundle ID to the given workflow. 1
ContentModerationTestTrait::createEditorialWorkflow protected function Creates the editorial workflow. 1
ContentTypeCreationTrait::createContentType protected function Creates a custom content type based on default settings. 1
DrupalTestCaseTrait::$root protected property The Drupal root directory.
DrupalTestCaseTrait::checkErrorHandlerOnTearDown public function Checks the test error handler after test execution. 1
DrupalTestCaseTrait::getDrupalRoot Deprecated protected static function Returns the Drupal root directory. 1
DrupalTestCaseTrait::setDebugDumpHandler public static function Registers the dumper CLI handler when the DebugDump extension is enabled.
DrupalTestCaseTrait::setUpRoot final protected function Ensure that the $root property is set initially.
EntityDefinitionTestTrait::addBaseField protected function Adds a new base field to the 'entity_test_update' entity type.
EntityDefinitionTestTrait::addBaseFieldIndex protected function Adds a single-field index to the base field.
EntityDefinitionTestTrait::addBundleField protected function Adds a new bundle field to the 'entity_test_update' entity type.
EntityDefinitionTestTrait::addEntityIndex protected function Adds an index to the 'entity_test_update' entity type's base table.
EntityDefinitionTestTrait::addLongNameBaseField protected function Adds a long-named base field to the 'entity_test_update' entity type.
EntityDefinitionTestTrait::addRevisionableBaseField protected function Adds a new revisionable base field to the 'entity_test_update' entity type.
EntityDefinitionTestTrait::applyEntityUpdates protected function Applies all the detected valid changes.
EntityDefinitionTestTrait::deleteEntityType protected function Removes the entity type.
EntityDefinitionTestTrait::doEntityUpdate protected function Performs an entity type definition update.
EntityDefinitionTestTrait::doFieldUpdate protected function Performs a field storage definition update.
EntityDefinitionTestTrait::enableNewEntityType protected function Enables a new entity type definition.
EntityDefinitionTestTrait::getUpdatedEntityTypeDefinition protected function Returns an entity type definition, possibly updated to be rev or mul.
EntityDefinitionTestTrait::getUpdatedFieldStorageDefinitions protected function Returns the required rev / mul field definitions for an entity type.
EntityDefinitionTestTrait::makeBaseFieldEntityKey protected function Promotes a field to an entity key.
EntityDefinitionTestTrait::modifyBaseField protected function Modifies the new base field from 'string' to 'text'.
EntityDefinitionTestTrait::modifyBundleField protected function Modifies the new bundle field from 'string' to 'text'.
EntityDefinitionTestTrait::removeBaseField protected function Removes the new base field from the 'entity_test_update' entity type.
EntityDefinitionTestTrait::removeBaseFieldIndex protected function Removes the index added in addBaseFieldIndex().
EntityDefinitionTestTrait::removeBundleField protected function Removes the new bundle field from the 'entity_test_update' entity type.
EntityDefinitionTestTrait::removeEntityIndex protected function Removes the index added in addEntityIndex().
EntityDefinitionTestTrait::renameBaseTable protected function Renames the base table to 'entity_test_update_new'.
EntityDefinitionTestTrait::renameDataTable protected function Renames the data table to 'entity_test_update_data_new'.
EntityDefinitionTestTrait::renameRevisionBaseTable protected function Renames the revision table to 'entity_test_update_revision_new'.
EntityDefinitionTestTrait::renameRevisionDataTable protected function Renames the revision data table to 'entity_test_update_revision_data_new'.
EntityDefinitionTestTrait::resetEntityType protected function Resets the entity type definition.
EntityDefinitionTestTrait::updateEntityTypeToNotRevisionable protected function Updates the 'entity_test_update' entity type not revisionable.
EntityDefinitionTestTrait::updateEntityTypeToNotTranslatable protected function Updates the 'entity_test_update' entity type to not translatable.
EntityDefinitionTestTrait::updateEntityTypeToRevisionable protected function Updates the 'entity_test_update' entity type to revisionable.
EntityDefinitionTestTrait::updateEntityTypeToRevisionableAndTranslatable protected function Updates the test entity type to be revisionable and translatable.
EntityDefinitionTestTrait::updateEntityTypeToTranslatable protected function Updates the 'entity_test_update' entity type to translatable.
ExtensionListTestTrait::getModulePath protected function Gets the path for the specified module.
ExtensionListTestTrait::getThemePath protected function Gets the path for the specified theme.
HttpKernelUiHelperTrait::$mink protected property Mink session manager.
HttpKernelUiHelperTrait::assertSession public function Returns WebAssert object.
HttpKernelUiHelperTrait::buildUrl protected function Builds a URL from a system path or a URL object.
HttpKernelUiHelperTrait::clickLink protected function Follows a link by complete name.
HttpKernelUiHelperTrait::drupalGet protected function Retrieves a Drupal path.
HttpKernelUiHelperTrait::getDefaultDriverInstance protected function Gets an instance of the default Mink driver.
HttpKernelUiHelperTrait::getNodeElementsByXpath protected function Performs an xpath search on the contents of the internal browser.
HttpKernelUiHelperTrait::getSession public function Returns Mink session.
HttpKernelUiHelperTrait::getUrl protected function Gets the current URL from the browser.
HttpKernelUiHelperTrait::initMink protected function Initializes Mink sessions.
HttpKernelUiHelperTrait::rebuildContainer protected function Rebuilds the container.
KernelTestBase::$classLoader protected property The class loader.
KernelTestBase::$configImporter protected property The configuration importer.
KernelTestBase::$configSchemaCheckerExclusions protected static property An array of config object names that are excluded from schema checking. 4
KernelTestBase::$container protected property The test container.
KernelTestBase::$databasePrefix protected property The test database prefix.
KernelTestBase::$keyValue protected property The key_value service that must persist between container rebuilds.
KernelTestBase::$siteDirectory protected property The relative path to the test site directory.
KernelTestBase::$strictConfigSchema protected property Set to TRUE to strict check all configuration saved. 9
KernelTestBase::$usesSuperUserAccessPolicy protected property Set to TRUE to make user 1 a super user. 1
KernelTestBase::$vfsRoot protected property The virtual filesystem root directory.
KernelTestBase::assertPostConditions protected function 1
KernelTestBase::bootEnvironment protected function Bootstraps a basic test environment.
KernelTestBase::bootKernel protected function Bootstraps a kernel for a test. 1
KernelTestBase::config protected function Configuration accessor for tests. Returns non-overridden configuration.
KernelTestBase::disableModules protected function Disables modules for this test.
KernelTestBase::enableModules protected function Enables modules for this test. 2
KernelTestBase::getConfigSchemaExclusions protected function Gets the config schema exclusions for this test.
KernelTestBase::getDatabaseConnectionInfo protected function Returns the Database connection info to be used for this test. 3
KernelTestBase::getDatabasePrefix public function Gets the database prefix used for test isolation.
KernelTestBase::getExtensionsForModules private function Returns Extension objects for $modules to install.
KernelTestBase::getModulesToEnable protected static function Returns the modules to install for this test.
KernelTestBase::initFileCache protected function Initializes the FileCache component.
KernelTestBase::installConfig protected function Installs default configuration for a given list of modules.
KernelTestBase::installEntitySchema protected function Installs the storage schema for a specific entity type.
KernelTestBase::installSchema protected function Installs database tables from a module schema definition.
KernelTestBase::register public function Registers test-specific services. Overrides ServiceProviderInterface::register 42
KernelTestBase::render protected function Renders a render array. 1
KernelTestBase::setInstallProfile protected function Sets the install profile and rebuilds the container to update it.
KernelTestBase::setSetting protected function Sets an in-memory Settings variable.
KernelTestBase::setUpFilesystem protected function Sets up the filesystem, so things like the file directory. 3
KernelTestBase::tearDown protected function 10
KernelTestBase::tearDownCloseDatabaseConnection public function Additional tear down method to close the connection at the end.
KernelTestBase::vfsDump protected function Dumps the current state of the virtual filesystem to STDOUT.
KernelTestBase::__sleep public function Prevents serializing any properties.
RandomGeneratorTrait::getRandomGenerator protected function Gets the random generator for the utility methods.
RandomGeneratorTrait::randomMachineName protected function Generates a unique random string containing letters and numbers.
RandomGeneratorTrait::randomObject public function Generates a random PHP object.
RandomGeneratorTrait::randomString public function Generates a pseudo-random string of ASCII characters of codes 32 to 126.
StorageCopyTrait::replaceStorageContents protected static function Copy the configuration from one storage to another and remove stale items.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.