You are here

class WorkspaceIntegrationTest in Workspace 8.2

Tests a complete deployment scenario across different workspaces.

@group workspace

Hierarchy

Expanded class hierarchy of WorkspaceIntegrationTest

File

tests/src/Kernel/WorkspaceIntegrationTest.php, line 20

Namespace

Drupal\Tests\workspace\Kernel
View source
class WorkspaceIntegrationTest extends KernelTestBase {
  use ContentTypeCreationTrait;
  use EntityReferenceTestTrait;
  use NodeCreationTrait;
  use UserCreationTrait;
  use ViewResultAssertionTrait;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * An array of test workspaces, keyed by workspace ID.
   *
   * @var \Drupal\workspace\WorkspaceInterface[]
   */
  protected $workspaces = [];

  /**
   * Creation timestamp that should be incremented for each new entity.
   *
   * @var int
   */
  protected $createdTimestamp;

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'entity_test',
    'field',
    'filter',
    'node',
    'text',
    'user',
    'system',
    'views',
  ];

  /**
   * {@inheritdoc}
   */
  protected function setUp() {
    parent::setUp();
    $this->entityTypeManager = \Drupal::entityTypeManager();
    $this
      ->installConfig([
      'filter',
      'node',
      'system',
    ]);
    $this
      ->installSchema('system', [
      'key_value_expire',
      'sequences',
    ]);
    $this
      ->installSchema('node', [
      'node_access',
    ]);
    $this
      ->installEntitySchema('entity_test_mulrev');
    $this
      ->installEntitySchema('node');
    $this
      ->installEntitySchema('user');
    $this
      ->createContentType([
      'type' => 'page',
    ]);
    $this
      ->setCurrentUser($this
      ->createUser([
      'administer nodes',
    ]));

    // Create two nodes, a published and an unpublished one, so we can test the
    // behavior of the module with default/existing content.
    $this->createdTimestamp = \Drupal::time()
      ->getRequestTime();
    $this
      ->createNode([
      'title' => 'live - 1 - r1 - published',
      'created' => $this->createdTimestamp++,
      'status' => TRUE,
    ]);
    $this
      ->createNode([
      'title' => 'live - 2 - r2 - unpublished',
      'created' => $this->createdTimestamp++,
      'status' => FALSE,
    ]);
  }

  /**
   * Enables the Workspace module and creates two workspaces.
   */
  protected function initializeWorkspaceModule() {

    // Enable the Workspace module here instead of the static::$modules array so
    // we can test it with default content.
    $this
      ->enableModules([
      'workspace',
    ]);
    $this->container = \Drupal::getContainer();
    $this->entityTypeManager = \Drupal::entityTypeManager();
    $this
      ->installEntitySchema('workspace');
    $this
      ->installEntitySchema('workspace_association');

    // Create two workspaces by default, 'live' and 'stage'.
    $this->workspaces['live'] = Workspace::create([
      'id' => 'live',
    ]);
    $this->workspaces['live']
      ->save();
    $this->workspaces['stage'] = Workspace::create([
      'id' => 'stage',
      'target' => 'live',
    ]);
    $this->workspaces['stage']
      ->save();
    $permissions = [
      'administer nodes',
      'create workspace',
      'edit any workspace',
      'view any workspace',
    ];
    $this
      ->setCurrentUser($this
      ->createUser($permissions));
  }

  /**
   * Tests various scenarios for creating and deploying content in workspaces.
   */
  public function testWorkspaces() {
    $this
      ->initializeWorkspaceModule();

    // Notes about the structure of the test scenarios:
    // - a multi-dimensional array keyed by the workspace ID, then by the entity
    //   ID and finally by the revision ID.
    // - 'default_revision' indicates the entity revision that should be
    //   returned by entity_load(), non-revision entity queries and non-revision
    //   views *in a given workspace*, it does not indicate what is actually
    //   stored in the base and data entity tables.
    $test_scenarios = [];

    // The $expected_workspace_association array holds the revision IDs which
    // should be tracked by the Workspace Association entity type in each test
    // scenario, keyed by workspace ID.
    $expected_workspace_association = [];

    // In the initial state we have only the two revisions that were created
    // before the Workspace module was installed.
    $revision_state = [
      'live' => [
        1 => [
          1 => [
            'title' => 'live - 1 - r1 - published',
            'status' => TRUE,
            'default_revision' => TRUE,
          ],
        ],
        2 => [
          2 => [
            'title' => 'live - 2 - r2 - unpublished',
            'status' => FALSE,
            'default_revision' => TRUE,
          ],
        ],
      ],
      'stage' => [
        1 => [
          1 => [
            'title' => 'live - 1 - r1 - published',
            'status' => TRUE,
            'default_revision' => TRUE,
          ],
        ],
        2 => [
          2 => [
            'title' => 'live - 2 - r2 - unpublished',
            'status' => FALSE,
            'default_revision' => TRUE,
          ],
        ],
      ],
    ];
    $test_scenarios['initial_state'] = $revision_state;
    $expected_workspace_association['initial_state'] = [
      'stage' => [],
    ];

    // Unpublish node 1 in 'stage'. The new revision is also added to 'live' but
    // it is not the default revision.
    $revision_state = array_replace_recursive($revision_state, [
      'live' => [
        1 => [
          3 => [
            'title' => 'stage - 1 - r3 - unpublished',
            'status' => FALSE,
            'default_revision' => FALSE,
          ],
        ],
      ],
      'stage' => [
        1 => [
          1 => [
            'default_revision' => FALSE,
          ],
          3 => [
            'title' => 'stage - 1 - r3 - unpublished',
            'status' => FALSE,
            'default_revision' => TRUE,
          ],
        ],
      ],
    ]);
    $test_scenarios['unpublish_node_1_in_stage'] = $revision_state;
    $expected_workspace_association['unpublish_node_1_in_stage'] = [
      'stage' => [
        3,
      ],
    ];

    // Publish node 2 in 'stage'. The new revision is also added to 'live' but
    // it is not the default revision.
    $revision_state = array_replace_recursive($revision_state, [
      'live' => [
        2 => [
          4 => [
            'title' => 'stage - 2 - r4 - published',
            'status' => TRUE,
            'default_revision' => FALSE,
          ],
        ],
      ],
      'stage' => [
        2 => [
          2 => [
            'default_revision' => FALSE,
          ],
          4 => [
            'title' => 'stage - 2 - r4 - published',
            'status' => TRUE,
            'default_revision' => TRUE,
          ],
        ],
      ],
    ]);
    $test_scenarios['publish_node_2_in_stage'] = $revision_state;
    $expected_workspace_association['publish_node_2_in_stage'] = [
      'stage' => [
        3,
        4,
      ],
    ];

    // Adding a new unpublished node on 'stage' should create a single
    // unpublished revision on both 'stage' and 'live'.
    $revision_state = array_replace_recursive($revision_state, [
      'live' => [
        3 => [
          5 => [
            'title' => 'stage - 3 - r5 - unpublished',
            'status' => FALSE,
            'default_revision' => TRUE,
          ],
        ],
      ],
      'stage' => [
        3 => [
          5 => [
            'title' => 'stage - 3 - r5 - unpublished',
            'status' => FALSE,
            'default_revision' => TRUE,
          ],
        ],
      ],
    ]);
    $test_scenarios['add_unpublished_node_in_stage'] = $revision_state;
    $expected_workspace_association['add_unpublished_node_in_stage'] = [
      'stage' => [
        3,
        4,
        5,
      ],
    ];

    // Adding a new published node on 'stage' should create two revisions, an
    // unpublished revision on 'live' and a published one on 'stage'.
    $revision_state = array_replace_recursive($revision_state, [
      'live' => [
        4 => [
          6 => [
            'title' => 'stage - 4 - r6 - published',
            'status' => FALSE,
            'default_revision' => TRUE,
          ],
          7 => [
            'title' => 'stage - 4 - r6 - published',
            'status' => TRUE,
            'default_revision' => FALSE,
          ],
        ],
      ],
      'stage' => [
        4 => [
          6 => [
            'title' => 'stage - 4 - r6 - published',
            'status' => FALSE,
            'default_revision' => FALSE,
          ],
          7 => [
            'title' => 'stage - 4 - r6 - published',
            'status' => TRUE,
            'default_revision' => TRUE,
          ],
        ],
      ],
    ]);
    $test_scenarios['add_published_node_in_stage'] = $revision_state;
    $expected_workspace_association['add_published_node_in_stage'] = [
      'stage' => [
        3,
        4,
        5,
        6,
        7,
      ],
    ];

    // Deploying 'stage' to 'live' should simply make the latest revisions in
    // 'stage' the default ones in 'live'.
    $revision_state = array_replace_recursive($revision_state, [
      'live' => [
        1 => [
          1 => [
            'default_revision' => FALSE,
          ],
          3 => [
            'default_revision' => TRUE,
          ],
        ],
        2 => [
          2 => [
            'default_revision' => FALSE,
          ],
          4 => [
            'default_revision' => TRUE,
          ],
        ],
        // Node 3 has a single revision for both 'stage' and 'live' and it is
        // already the default revision in both of them.
        4 => [
          6 => [
            'default_revision' => FALSE,
          ],
          7 => [
            'default_revision' => TRUE,
          ],
        ],
      ],
    ]);
    $test_scenarios['push_stage_to_live'] = $revision_state;
    $expected_workspace_association['push_stage_to_live'] = [
      'stage' => [],
    ];

    // Check the initial state after the module was installed.
    $this
      ->assertWorkspaceStatus($test_scenarios['initial_state'], 'node');
    $this
      ->assertWorkspaceAssociation($expected_workspace_association['initial_state'], 'node');

    // Unpublish node 1 in 'stage'.
    $this
      ->switchToWorkspace('stage');
    $node = $this->entityTypeManager
      ->getStorage('node')
      ->load(1);
    $node
      ->setTitle('stage - 1 - r3 - unpublished');
    $node
      ->setUnpublished();
    $node
      ->save();
    $this
      ->assertWorkspaceStatus($test_scenarios['unpublish_node_1_in_stage'], 'node');
    $this
      ->assertWorkspaceAssociation($expected_workspace_association['unpublish_node_1_in_stage'], 'node');

    // Publish node 2 in 'stage'.
    $this
      ->switchToWorkspace('stage');
    $node = $this->entityTypeManager
      ->getStorage('node')
      ->load(2);
    $node
      ->setTitle('stage - 2 - r4 - published');
    $node
      ->setPublished();
    $node
      ->save();
    $this
      ->assertWorkspaceStatus($test_scenarios['publish_node_2_in_stage'], 'node');
    $this
      ->assertWorkspaceAssociation($expected_workspace_association['publish_node_2_in_stage'], 'node');

    // Add a new unpublished node on 'stage'.
    $this
      ->switchToWorkspace('stage');
    $this
      ->createNode([
      'title' => 'stage - 3 - r5 - unpublished',
      'created' => $this->createdTimestamp++,
      'status' => FALSE,
    ]);
    $this
      ->assertWorkspaceStatus($test_scenarios['add_unpublished_node_in_stage'], 'node');
    $this
      ->assertWorkspaceAssociation($expected_workspace_association['add_unpublished_node_in_stage'], 'node');

    // Add a new published node on 'stage'.
    $this
      ->switchToWorkspace('stage');
    $this
      ->createNode([
      'title' => 'stage - 4 - r6 - published',
      'created' => $this->createdTimestamp++,
      'status' => TRUE,
    ]);
    $this
      ->assertWorkspaceStatus($test_scenarios['add_published_node_in_stage'], 'node');
    $this
      ->assertWorkspaceAssociation($expected_workspace_association['add_published_node_in_stage'], 'node');

    // Deploy 'stage' to 'live'.
    $stage_repository_handler = $this->workspaces['stage']
      ->getRepositoryHandler();

    // Check which revisions need to be pushed.
    $expected = [
      'node' => [
        3 => 1,
        4 => 2,
        5 => 3,
        7 => 4,
      ],
    ];
    $this
      ->assertEquals($expected, $stage_repository_handler
      ->getDifferringRevisionIdsOnSource());
    $stage_repository_handler
      ->push();
    $this
      ->assertWorkspaceStatus($test_scenarios['push_stage_to_live'], 'node');
    $this
      ->assertWorkspaceAssociation($expected_workspace_association['push_stage_to_live'], 'node');

    // Check that there are no more revisions to push.
    $this
      ->assertEmpty($stage_repository_handler
      ->getDifferringRevisionIdsOnSource());
  }

  /**
   * Tests the Entity Query relationship API with workspaces.
   */
  public function testEntityQueryRelationship() {
    $this
      ->initializeWorkspaceModule();

    // Add an entity reference field that targets 'entity_test_mulrev' entities.
    $this
      ->createEntityReferenceField('node', 'page', 'field_test_entity', 'Test entity reference', 'entity_test_mulrev');

    // Add an entity reference field that targets 'node' entities so we can test
    // references to the same base tables.
    $this
      ->createEntityReferenceField('node', 'page', 'field_test_node', 'Test node reference', 'node');
    $this
      ->switchToWorkspace('live');
    $node_1 = $this
      ->createNode([
      'title' => 'live node 1',
    ]);
    $entity_test = EntityTestMulRev::create([
      'name' => 'live entity_test_mulrev',
      'non_rev_field' => 'live non-revisionable value',
    ]);
    $entity_test
      ->save();
    $node_2 = $this
      ->createNode([
      'title' => 'live node 2',
      'field_test_entity' => $entity_test
        ->id(),
      'field_test_node' => $node_1
        ->id(),
    ]);

    // Switch to the 'stage' workspace and change some values for the referenced
    // entities.
    $this
      ->switchToWorkspace('stage');
    $node_1->title->value = 'stage node 1';
    $node_1
      ->save();
    $node_2->title->value = 'stage node 2';
    $node_2
      ->save();
    $entity_test->name->value = 'stage entity_test_mulrev';
    $entity_test->non_rev_field->value = 'stage non-revisionable value';
    $entity_test
      ->save();

    // Make sure that we're requesting the default revision.
    $query = $this->entityTypeManager
      ->getStorage('node')
      ->getQuery();
    $query
      ->currentRevision();
    $query
      ->condition('title', 'stage node 2')
      ->condition('revision_uid', $node_2
      ->getRevisionUserId())
      ->condition('type', $node_2
      ->bundle())
      ->condition('uuid', $node_2
      ->uuid());

    // Add conditions for a reference to the same entity type.
    $query
      ->condition('field_test_node.entity.title', 'stage node 1')
      ->condition('field_test_node.entity.revision_uid', $node_1
      ->getRevisionUserId())
      ->condition('field_test_node.entity.type', $node_1
      ->bundle())
      ->condition('field_test_node.entity.uuid', $node_1
      ->uuid());

    // Add conditions for a reference to a different entity type.
    $query
      ->condition('field_test_entity.entity.name', 'stage entity_test_mulrev')
      ->condition('field_test_entity.entity.non_rev_field', 'stage non-revisionable value')
      ->condition('field_test_entity.entity.uuid', $entity_test
      ->uuid());
    $result = $query
      ->execute();
    $this
      ->assertSame([
      $node_2
        ->getRevisionId() => $node_2
        ->id(),
    ], $result);
  }

  /**
   * Checks entity load, entity queries and views results for a test scenario.
   *
   * @param array $expected
   *   An array of expected values, as defined in ::testWorkspaces().
   * @param string $entity_type_id
   *   The ID of the entity type that is being tested.
   */
  protected function assertWorkspaceStatus(array $expected, $entity_type_id) {
    $expected = $this
      ->flattenExpectedValues($expected, $entity_type_id);
    $entity_keys = $this->entityTypeManager
      ->getDefinition($entity_type_id)
      ->getKeys();
    foreach ($expected as $workspace_id => $expected_values) {
      $this
        ->switchToWorkspace($workspace_id);

      // Check that default revisions are swapped with the workspace revision.
      $this
        ->assertEntityLoad($expected_values, $entity_type_id);

      // Check that non-default revisions are not changed.
      $this
        ->assertEntityRevisionLoad($expected_values, $entity_type_id);

      // Check that entity queries return the correct results.
      $this
        ->assertEntityQuery($expected_values, $entity_type_id);

      // Check that the 'Frontpage' view only shows published content that is
      // also considered as the default revision in the given workspace.
      $expected_frontpage = array_filter($expected_values, function ($expected_value) {
        return $expected_value['status'] === TRUE && $expected_value['default_revision'] === TRUE;
      });

      // The 'Frontpage' view will output nodes in reverse creation order.
      usort($expected_frontpage, function ($a, $b) {
        return $b['nid'] - $a['nid'];
      });
      $view = Views::getView('frontpage');
      $view
        ->execute();
      $this
        ->assertIdenticalResultset($view, $expected_frontpage, [
        'nid' => 'nid',
      ]);
      $rendered_view = $view
        ->render('page_1');
      $output = \Drupal::service('renderer')
        ->renderRoot($rendered_view);
      $this
        ->setRawContent($output);
      foreach ($expected_values as $expected_entity_values) {
        if ($expected_entity_values[$entity_keys['published']] === TRUE && $expected_entity_values['default_revision'] === TRUE) {
          $this
            ->assertRaw($expected_entity_values[$entity_keys['label']]);
        }
        elseif ($workspace_id != 'stage' && $expected_entity_values[$entity_keys['id']] != 4) {
          $this
            ->assertNoRaw($expected_entity_values[$entity_keys['label']]);
        }
      }
    }
  }

  /**
   * Asserts that default revisions are properly swapped in a workspace.
   *
   * @param array $expected_values
   *   An array of expected values, as defined in ::testWorkspaces().
   * @param string $entity_type_id
   *   The ID of the entity type to check.
   */
  protected function assertEntityLoad(array $expected_values, $entity_type_id) {

    // Filter the expected values so we can check only the default revisions.
    $expected_default_revisions = array_filter($expected_values, function ($expected_value) {
      return $expected_value['default_revision'] === TRUE;
    });
    $entity_keys = $this->entityTypeManager
      ->getDefinition($entity_type_id)
      ->getKeys();
    $id_key = $entity_keys['id'];
    $revision_key = $entity_keys['revision'];
    $label_key = $entity_keys['label'];
    $published_key = $entity_keys['published'];

    // Check \Drupal\Core\Entity\EntityStorageInterface::loadMultiple().

    /** @var \Drupal\Core\Entity\EntityInterface[]|\Drupal\Core\Entity\RevisionableInterface[]|\Drupal\Core\Entity\EntityPublishedInterface[] $entities */
    $entities = $this->entityTypeManager
      ->getStorage($entity_type_id)
      ->loadMultiple(array_column($expected_default_revisions, $id_key));
    foreach ($expected_default_revisions as $expected_default_revision) {
      $entity_id = $expected_default_revision[$id_key];
      $this
        ->assertEquals($expected_default_revision[$revision_key], $entities[$entity_id]
        ->getRevisionId());
      $this
        ->assertEquals($expected_default_revision[$label_key], $entities[$entity_id]
        ->label());
      $this
        ->assertEquals($expected_default_revision[$published_key], $entities[$entity_id]
        ->isPublished());
    }

    // Check \Drupal\Core\Entity\EntityStorageInterface::loadUnchanged().
    foreach ($expected_default_revisions as $expected_default_revision) {

      /** @var \Drupal\Core\Entity\EntityInterface|\Drupal\Core\Entity\RevisionableInterface|\Drupal\Core\Entity\EntityPublishedInterface $entity */
      $entity = $this->entityTypeManager
        ->getStorage($entity_type_id)
        ->loadUnchanged($expected_default_revision[$id_key]);
      $this
        ->assertEquals($expected_default_revision[$revision_key], $entity
        ->getRevisionId());
      $this
        ->assertEquals($expected_default_revision[$label_key], $entity
        ->label());
      $this
        ->assertEquals($expected_default_revision[$published_key], $entity
        ->isPublished());
    }
  }

  /**
   * Asserts that non-default revisions are not changed.
   *
   * @param array $expected_values
   *   An array of expected values, as defined in ::testWorkspaces().
   * @param string $entity_type_id
   *   The ID of the entity type to check.
   */
  protected function assertEntityRevisionLoad(array $expected_values, $entity_type_id) {
    $entity_keys = $this->entityTypeManager
      ->getDefinition($entity_type_id)
      ->getKeys();
    $id_key = $entity_keys['id'];
    $revision_key = $entity_keys['revision'];
    $label_key = $entity_keys['label'];
    $published_key = $entity_keys['published'];

    /** @var \Drupal\Core\Entity\EntityInterface[]|\Drupal\Core\Entity\RevisionableInterface[]|\Drupal\Core\Entity\EntityPublishedInterface[] $entities */
    $entities = $this->entityTypeManager
      ->getStorage($entity_type_id)
      ->loadMultipleRevisions(array_column($expected_values, $revision_key));
    foreach ($expected_values as $expected_revision) {
      $revision_id = $expected_revision[$revision_key];
      $this
        ->assertEquals($expected_revision[$id_key], $entities[$revision_id]
        ->id());
      $this
        ->assertEquals($expected_revision[$revision_key], $entities[$revision_id]
        ->getRevisionId());
      $this
        ->assertEquals($expected_revision[$label_key], $entities[$revision_id]
        ->label());
      $this
        ->assertEquals($expected_revision[$published_key], $entities[$revision_id]
        ->isPublished());
    }
  }

  /**
   * Asserts that entity queries are giving the correct results in a workspace.
   *
   * @param array $expected_values
   *   An array of expected values, as defined in ::testWorkspaces().
   * @param string $entity_type_id
   *   The ID of the entity type to check.
   */
  protected function assertEntityQuery(array $expected_values, $entity_type_id) {
    $storage = $this->entityTypeManager
      ->getStorage($entity_type_id);
    $entity_keys = $this->entityTypeManager
      ->getDefinition($entity_type_id)
      ->getKeys();
    $id_key = $entity_keys['id'];
    $revision_key = $entity_keys['revision'];
    $label_key = $entity_keys['label'];
    $published_key = $entity_keys['published'];

    // Filter the expected values so we can check only the default revisions.
    $expected_default_revisions = array_filter($expected_values, function ($expected_value) {
      return $expected_value['default_revision'] === TRUE;
    });

    // Check entity query counts.
    $result = $storage
      ->getQuery()
      ->count()
      ->execute();
    $this
      ->assertEquals(count($expected_default_revisions), $result);
    $result = $storage
      ->getAggregateQuery()
      ->count()
      ->execute();
    $this
      ->assertEquals(count($expected_default_revisions), $result);

    // Check entity queries with no conditions.
    $result = $storage
      ->getQuery()
      ->execute();
    $expected_result = array_combine(array_column($expected_default_revisions, $revision_key), array_column($expected_default_revisions, $id_key));
    $this
      ->assertEquals($expected_result, $result);

    // Check querying each revision individually.
    foreach ($expected_values as $expected_value) {
      $query = $storage
        ->getQuery();
      $query
        ->condition($entity_keys['id'], $expected_value[$id_key])
        ->condition($entity_keys['label'], $expected_value[$label_key])
        ->condition($entity_keys['published'], (int) $expected_value[$published_key]);

      // If the entity is not expected to be the default revision, we need to
      // query all revisions if we want to find it.
      if (!$expected_value['default_revision']) {
        $query
          ->allRevisions();
      }
      $result = $query
        ->execute();
      $this
        ->assertEquals([
        $expected_value[$revision_key] => $expected_value[$id_key],
      ], $result);
    }
  }

  /**
   * Checks the workspace_association entries for a test scenario.
   *
   * @param array $expected
   *   An array of expected values, as defined in ::testWorkspaces().
   * @param string $entity_type_id
   *   The ID of the entity type that is being tested.
   */
  protected function assertWorkspaceAssociation(array $expected, $entity_type_id) {

    /** @var \Drupal\workspace\WorkspaceAssociationStorageInterface $workspace_association_storage */
    $workspace_association_storage = $this->entityTypeManager
      ->getStorage('workspace_association');
    foreach ($expected as $workspace_id => $expected_tracked_revision_ids) {
      $tracked_entities = $workspace_association_storage
        ->getTrackedEntities($workspace_id, TRUE);
      $tracked_revision_ids = isset($tracked_entities[$entity_type_id]) ? $tracked_entities[$entity_type_id] : [];
      $this
        ->assertEquals($expected_tracked_revision_ids, array_keys($tracked_revision_ids));
    }
  }

  /**
   * Sets a given workspace as active.
   *
   * @param string $workspace_id
   *   The ID of the workspace to switch to.
   */
  protected function switchToWorkspace($workspace_id) {

    // Switch the test runner's context to the specified workspace.
    $workspace = $this->entityTypeManager
      ->getStorage('workspace')
      ->load($workspace_id);
    \Drupal::service('workspace.manager')
      ->setActiveWorkspace($workspace);
  }

  /**
   * Flattens the expectations array defined by testWorkspaces().
   *
   * @param array $expected
   *   An array as defined by testWorkspaces().
   * @param string $entity_type_id
   *   The ID of the entity type that is being tested.
   *
   * @return array
   *   An array where all the entity IDs and revision IDs are merged inside each
   *   expected values array.
   */
  protected function flattenExpectedValues(array $expected, $entity_type_id) {
    $flattened = [];
    $entity_keys = $this->entityTypeManager
      ->getDefinition($entity_type_id)
      ->getKeys();
    foreach ($expected as $workspace_id => $workspace_values) {
      foreach ($workspace_values as $entity_id => $entity_revisions) {
        foreach ($entity_revisions as $revision_id => $revision_values) {
          $flattened[$workspace_id][] = [
            $entity_keys['id'] => $entity_id,
            $entity_keys['revision'] => $revision_id,
          ] + $revision_values;
        }
      }
    }
    return $flattened;
  }

}

Members

Namesort descending Modifiers Type Description 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. 1
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::assertFieldById protected function Asserts that a field exists with the given ID and value.
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::assertFieldChecked protected function Asserts that a checkbox field in the current page is checked.
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::assertNoDuplicateIds protected function Asserts that each HTML ID is used for just a single element.
AssertContentTrait::assertNoEscaped protected function Passes if the raw text IS NOT found escaped on the loaded page, fail otherwise.
AssertContentTrait::assertNoField protected function Asserts that a field does not exist with the given name or ID.
AssertContentTrait::assertNoFieldById protected function Asserts that a field does not exist with the given ID and value.
AssertContentTrait::assertNoFieldByName protected function Asserts that a field does not exist with the given name and value.
AssertContentTrait::assertNoFieldByXPath protected function Asserts that a field does not exist or its value does not match, by XPath.
AssertContentTrait::assertNoFieldChecked protected function Asserts that a checkbox field in the current page is not checked.
AssertContentTrait::assertNoLink protected function Passes if a link with the specified label is not found.
AssertContentTrait::assertNoLinkByHref protected function Passes if a link containing a given href (part) is not found.
AssertContentTrait::assertNoLinkByHrefInMainRegion protected function Passes if a link containing a given href is not found in the main region.
AssertContentTrait::assertNoOption protected function Asserts that a select option in the current page does not exist.
AssertContentTrait::assertNoOptionSelected protected function Asserts that a select option in the current page is not checked.
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::assertNoTitle protected function Pass if the page title is not the given string.
AssertContentTrait::assertNoUniqueText protected function Passes if the text is found MORE THAN ONCE on the text version of the page.
AssertContentTrait::assertOption protected function Asserts that a select option in the current page exists.
AssertContentTrait::assertOptionByText protected function Asserts that a select option with the visible text exists.
AssertContentTrait::assertOptionSelected protected function Asserts that a select option in the current page is checked.
AssertContentTrait::assertOptionSelectedWithDrupalSelector protected function Asserts that a select option in the current page is checked.
AssertContentTrait::assertOptionWithDrupalSelector protected function Asserts that a select option in the current page exists.
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::assertTextPattern protected function Asserts that a Perl regex pattern is found in the plain-text content.
AssertContentTrait::assertThemeOutput protected function Asserts themed output.
AssertContentTrait::assertTitle protected function Pass if the page title is the given string.
AssertContentTrait::assertUniqueText protected function Passes if the text is found ONLY ONCE on the text version of the page.
AssertContentTrait::assertUniqueTextHelper protected function Helper for assertUniqueText and assertNoUniqueText.
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::getUrl protected function Get the current URL from the cURL handler. 1
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.
AssertHelperTrait::castSafeStrings protected static function Casts MarkupInterface objects into strings.
AssertLegacyTrait::assert protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use self::assertTrue() instead.
AssertLegacyTrait::assertEqual protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use self::assertEquals() instead.
AssertLegacyTrait::assertIdentical protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use self::assertSame() instead.
AssertLegacyTrait::assertIdenticalObject protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use self::assertEquals() instead.
AssertLegacyTrait::assertNotEqual protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use self::assertNotEquals() instead.
AssertLegacyTrait::assertNotIdentical protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use self::assertNotSame() instead.
AssertLegacyTrait::pass protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use self::assertTrue() instead.
AssertLegacyTrait::verbose protected function
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.
ContentTypeCreationTrait::createContentType protected function Creates a custom content type based on default settings. 1
EntityReferenceTestTrait::createEntityReferenceField protected function Creates a field of an entity reference field storage on the specified bundle.
KernelTestBase::$backupGlobals protected property Back up and restore any global variables that may be changed by tests.
KernelTestBase::$backupStaticAttributes protected property Back up and restore static class properties that may be changed by tests.
KernelTestBase::$backupStaticAttributesBlacklist protected property Contains a few static class properties for performance.
KernelTestBase::$classLoader protected property
KernelTestBase::$configImporter protected property @todo Move into Config test base class. 7
KernelTestBase::$configSchemaCheckerExclusions protected static property An array of config object names that are excluded from schema checking.
KernelTestBase::$container protected property
KernelTestBase::$databasePrefix protected property
KernelTestBase::$preserveGlobalState protected property Do not forward any global state from the parent process to the processes that run the actual tests.
KernelTestBase::$root protected property The app root.
KernelTestBase::$runTestInSeparateProcess protected property Kernel tests are run in separate processes because they allow autoloading of code from extensions. Running the test in a separate process isolates this behavior from other tests. Subclasses should not override this property.
KernelTestBase::$siteDirectory protected property
KernelTestBase::$strictConfigSchema protected property Set to TRUE to strict check all configuration saved. 6
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 private function Bootstraps a kernel for a test.
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.
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. 1
KernelTestBase::getDatabasePrefix public function
KernelTestBase::getExtensionsForModules private function Returns Extension objects for $modules to enable.
KernelTestBase::getModulesToEnable private static function Returns the modules to enable 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::isTestInIsolation Deprecated protected function Returns whether the current test method is running in a separate process.
KernelTestBase::prepareTemplate protected function
KernelTestBase::register public function Registers test-specific services. Overrides ServiceProviderInterface::register 26
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::setUpBeforeClass public static function 1
KernelTestBase::setUpFilesystem protected function Sets up the filesystem, so things like the file directory. 2
KernelTestBase::stop protected function Stops test execution.
KernelTestBase::tearDown protected function 6
KernelTestBase::tearDownCloseDatabaseConnection public function @after
KernelTestBase::vfsDump protected function Dumps the current state of the virtual filesystem to STDOUT.
KernelTestBase::__get Deprecated public function BC: Automatically resolve former KernelTestBase class properties.
KernelTestBase::__sleep public function Prevents serializing any properties.
NodeCreationTrait::createNode protected function Creates a node based on default settings.
NodeCreationTrait::getNodeByTitle public function Get a node from the database based on its title.
PhpunitCompatibilityTrait::getMock Deprecated public function Returns a mock object for the specified class using the available method.
PhpunitCompatibilityTrait::setExpectedException Deprecated public function Compatibility layer for PHPUnit 6 to support PHPUnit 4 code.
RandomGeneratorTrait::$randomGenerator protected property The random generator.
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. 1
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.
RandomGeneratorTrait::randomStringValidate public function Callback for random string validation.
StorageCopyTrait::replaceStorageContents protected static function Copy the configuration from one storage to another and remove stale items.
TestRequirementsTrait::checkModuleRequirements private function Checks missing module requirements.
TestRequirementsTrait::checkRequirements protected function Check module requirements for the Drupal use case. 1
TestRequirementsTrait::getDrupalRoot protected static function Returns the Drupal root directory.
UserCreationTrait::checkPermissions protected function Checks whether a given list of permission names is valid.
UserCreationTrait::createAdminRole protected function Creates an administrative role.
UserCreationTrait::createRole protected function Creates a role with specified permissions.
UserCreationTrait::createUser protected function Create a user with a given set of permissions.
UserCreationTrait::grantPermissions protected function Grant permissions to a user role.
UserCreationTrait::setCurrentUser protected function Switch the current logged in user.
UserCreationTrait::setUpCurrentUser protected function Creates a random user account and sets it as current user.
ViewResultAssertionTrait::assertIdenticalResultset protected function Verifies that a result set returned by a View matches expected values.
ViewResultAssertionTrait::assertIdenticalResultsetHelper protected function Performs View result assertions.
ViewResultAssertionTrait::assertNotIdenticalResultset protected function Verifies that a result set returned by a View differs from certain values.
WorkspaceIntegrationTest::$createdTimestamp protected property Creation timestamp that should be incremented for each new entity.
WorkspaceIntegrationTest::$entityTypeManager protected property The entity type manager.
WorkspaceIntegrationTest::$modules protected static property Modules to enable. Overrides KernelTestBase::$modules
WorkspaceIntegrationTest::$workspaces protected property An array of test workspaces, keyed by workspace ID.
WorkspaceIntegrationTest::assertEntityLoad protected function Asserts that default revisions are properly swapped in a workspace.
WorkspaceIntegrationTest::assertEntityQuery protected function Asserts that entity queries are giving the correct results in a workspace.
WorkspaceIntegrationTest::assertEntityRevisionLoad protected function Asserts that non-default revisions are not changed.
WorkspaceIntegrationTest::assertWorkspaceAssociation protected function Checks the workspace_association entries for a test scenario.
WorkspaceIntegrationTest::assertWorkspaceStatus protected function Checks entity load, entity queries and views results for a test scenario.
WorkspaceIntegrationTest::flattenExpectedValues protected function Flattens the expectations array defined by testWorkspaces().
WorkspaceIntegrationTest::initializeWorkspaceModule protected function Enables the Workspace module and creates two workspaces.
WorkspaceIntegrationTest::setUp protected function Overrides KernelTestBase::setUp
WorkspaceIntegrationTest::switchToWorkspace protected function Sets a given workspace as active.
WorkspaceIntegrationTest::testEntityQueryRelationship public function Tests the Entity Query relationship API with workspaces.
WorkspaceIntegrationTest::testWorkspaces public function Tests various scenarios for creating and deploying content in workspaces.