You are here

class EntityBrowserContext in Lightning Media 8.2

Same name and namespace in other branches
  1. 8 tests/contexts/EntityBrowserContext.behat.inc \Acquia\LightningExtension\Context\EntityBrowserContext
  2. 8.3 tests/contexts/EntityBrowserContext.behat.inc \Acquia\LightningExtension\Context\EntityBrowserContext

Contains step definitions for interacting with entity browser instances.

@internal This class is part of Lightning's internal testing code. It is not an API and should not be extended. This class will be marked final, and all protected members will be made private, in Lightning Media 3.x.

Hierarchy

  • class \Acquia\LightningExtension\Context\EntityBrowserContext extends \Drupal\DrupalExtension\Context\DrupalSubContextBase uses \Acquia\LightningExtension\Context\AwaitTrait

Expanded class hierarchy of EntityBrowserContext

File

tests/contexts/EntityBrowserContext.behat.inc, line 20

Namespace

Acquia\LightningExtension\Context
View source
class EntityBrowserContext extends DrupalSubContextBase {
  use AwaitTrait;

  /**
   * Indicates if the current scenario uses JavaScript.
   *
   * @var bool
   */
  private $isJS;

  /**
   * Performs pre-scenario tasks.
   *
   * @BeforeScenario
   */
  public function setUp(ScenarioScope $scope) {

    // Check if the feature or scenario has the 'javascript' tag.
    $tags = array_merge($scope
      ->getScenario()
      ->getTags(), $scope
      ->getFeature()
      ->getTags());
    $this->isJS = in_array('javascript', $tags, TRUE);
  }

  /**
   * Gets all items in an entity browser.
   *
   * @param string $browser_id
   *   (optional) The entity browser ID.
   *
   * @return \Behat\Mink\Element\NodeElement[]
   *   An array of items in the entity browser.
   */
  protected function getItems($browser_id = NULL) {
    if ($browser_id) {
      $selector = 'form#entity-browser-' . Html::cleanCssIdentifier($browser_id) . '-form';
    }
    else {
      $selector = 'form[data-entity-browser-uuid]';
    }
    return $this
      ->assertSession()
      ->elementExists('css', $selector)
      ->findAll('css', '[data-selectable]');
  }

  /**
   * Selects an item in an entity browser view.
   *
   * @param int $n
   *   The one-based index of the item to select.
   * @param string $browser_id
   *   (optional) The entity browser ID.
   *
   * @throws \Behat\Mink\Exception\ExpectationException if the entity browser
   * contains fewer than $n items.
   *
   * @When I select item :n
   * @When I select item :n from the entity browser
   * @When I select item :n from the :browser_id entity browser
   */
  public function select($n, $browser_id = NULL) {
    $items = $this
      ->getItems($browser_id);
    if ($n > count($items)) {
      throw new ExpectationException("Expected at least {$n} item(s) in the {$browser_id} entity browser.", $this
        ->getSession()
        ->getDriver());
    }
    else {
      $items[--$n]
        ->click();
    }
  }

  /**
   * Asserts that a certain number of items are visible in the entity browser.
   *
   * @param int $n
   *   The number of items that should be visible.
   * @param string $browser_id
   *   (optional) The entity browser ID.
   *
   * @throws ExpectationException if the actual number of items in the entity
   * browser does not match the expected number.
   *
   * @Then I should see :n item(s) in the entity browser
   */
  public function assertCount($n, $browser_id = NULL) {
    $count = count($this
      ->getItems($browser_id));
    if ($count !== (int) $n) {
      throw new ExpectationException("Expected {$n} items in the {$browser_id} entity browser, but there were {$count}.", $this
        ->getSession()
        ->getDriver());
    }
  }

  /**
   * Clicks on a tab with the specified text in an active entity browser and
   * waits for it to load.
   *
   * @param string $tab
   *   The text of the tab to switch to.
   *
   * @When I switch to the :tab Entity Browser tab
   */
  public function switchToEBTab($tab) {
    $this
      ->assertSession()
      ->elementExists('css', 'nav.eb-tabs')
      ->clickLink($tab);

    // I don't see any way to assert the tab specifically has loaded. So,
    // instead we just wait a reasonable amount of time.
    sleep(5);
  }

  /**
   * Submits the entity browser.
   *
   * @When I submit the entity browser
   */
  public function submit() {
    $session = $this
      ->getSession();

    // @TODO: Make this smarter, because we can't be sure that #edit-submit
    // exists at all, or that it's the correct submit button.
    $button = $this
      ->assertSession()
      ->elementExists('css', '#edit-submit')
      ->getXpath();
    $frame = $session
      ->evaluateScript('window.name') ?: $session
      ->evaluateScript('window.active_iframe.name');
    assert(!empty($frame));

    // Switch out of the iFrame, because it will be destroyed as soon as we
    // press the button.
    $session
      ->switchToIFrame();
    $js = <<<END
document.evaluate('{<span class="php-variable">$button</span>}', window.{<span class="php-variable">$frame</span>}.document, null).iterateNext().click();
END;
    $session
      ->executeScript($js);
    $this
      ->awaitAjax();
  }

  /**
   * Opens an entity browser.
   *
   * @param string $id
   *   The entity browser ID.
   */
  public function open($id) {
    $this->isJS ? $this
      ->openJS($id) : $this
      ->openNoJS($id);
  }

  /**
   * Opens an entity browser using JavaScript.
   *
   * @param string $id
   *   The entity browser ID.
   */
  private function openJS($id) {
    $settings = $this
      ->getEntityBrowserSettings($id);
    $this
      ->assertSession()
      ->elementExists('css', '.entity-browser-handle[data-uuid="' . $settings['uuid'] . '"]')
      ->click();
    $frame = "window.entity_browser_iframe_{$id}";
    $this
      ->awaitExpression($frame);
    $this
      ->awaitExpression("{$frame}.document.readyState === 'complete'");
  }

  /**
   * Opens an entity browser without using JavaScript.
   *
   * @param string $id
   *   The entity browser ID.
   */
  private function openNoJS($id) {
    $settings = $this
      ->getEntityBrowserSettings($id);
    Assert::notEmpty($settings['src']);
    $this
      ->visitPath($settings['src']);
  }

  /**
   * Returns settings for a single entity browser.
   *
   * @param string $id
   *   The entity browser ID (not UUID).
   *
   * @return array
   *   The settings for the entity browser.
   *
   * @throws \Exception
   *   If there is not exactly one entity browser with the given ID.
   */
  private function getEntityBrowserSettings($id) {
    $filter = function (array $settings) use ($id) {
      return $settings['entity_browser_id'] === $id;
    };
    $settings = array_filter($this
      ->getAllEntityBrowserSettings(), $filter);
    Assert::count($settings, 1);
    return reset($settings);
  }

  /**
   * Returns settings for all entity browser instances on the page.
   *
   * @return array[]
   *   The settings for all entity browser instances, keyed by UUID.
   */
  private function getAllEntityBrowserSettings() {
    $settings = $this
      ->getAllSettings();
    Assert::isArray($settings['entity_browser']);
    Assert::notEmpty($settings['entity_browser']);
    $display_types = \Drupal::service('plugin.manager.entity_browser.display')
      ->getDefinitions();
    $settings = array_intersect_key($settings['entity_browser'], $display_types);
    $all = [];
    foreach ($settings as $display_type => $instances) {
      foreach ($instances as $uuid => $instance) {
        $instance['display_type'] = $display_type;
        $instance['uuid'] = $uuid;
        $all[$uuid] = $instance;
      }
    }
    return $all;
  }

  /**
   * Returns all Drupal JavaScript settings on the page.
   *
   * @return mixed[]
   *   The decoded settings.
   */
  private function getAllSettings() {
    $settings = $this
      ->assertSession()
      ->elementExists('css', 'script[type="application/json"][data-drupal-selector="drupal-settings-json"]')
      ->getText();
    return Json::decode($settings);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
EntityBrowserContext::$isJS private property Indicates if the current scenario uses JavaScript.
EntityBrowserContext::assertCount public function Asserts that a certain number of items are visible in the entity browser.
EntityBrowserContext::getAllEntityBrowserSettings private function Returns settings for all entity browser instances on the page.
EntityBrowserContext::getAllSettings private function Returns all Drupal JavaScript settings on the page.
EntityBrowserContext::getEntityBrowserSettings private function Returns settings for a single entity browser.
EntityBrowserContext::getItems protected function Gets all items in an entity browser.
EntityBrowserContext::open public function Opens an entity browser.
EntityBrowserContext::openJS private function Opens an entity browser using JavaScript.
EntityBrowserContext::openNoJS private function Opens an entity browser without using JavaScript.
EntityBrowserContext::select public function Selects an item in an entity browser view.
EntityBrowserContext::setUp public function Performs pre-scenario tasks.
EntityBrowserContext::submit public function Submits the entity browser.
EntityBrowserContext::switchToEBTab public function Clicks on a tab with the specified text in an active entity browser and waits for it to load.