I'm facing the following issue while writing tests for EntityEmbedDialog. I was trying to ensure that the form successfully shows the fields for 'embed' step when it's submitted for 'select' step. From what it seems, the form still displays the 'select' step even after I submit it using drupalPostAjaxForm(). Also, it throws no errors if I submit an invalid entity id. Here's the code for the test:

/**
 * @file
 * Contains \Drupal\entity_embed\Tests\EntityEmbedDialogTest.
 */

namespace Drupal\entity_embed\Tests;

/**
 * Tests the entity_embed dialog controller and route.
 *
 * @group entity_embed
 */
class EntityEmbedDialogTest extends EntityEmbedTestBase {

  /**
   * Base URL of the dialog route.
   *
   * @var string
   */
  protected $dialog_url;

  protected function setUp() {
    parent::setUp();

    // Define URL that will be used to access the entity embed dialog.
    $this->dialog_url = 'entity-embed/dialog/entity-embed/';
  }

  /**
   * Tests the entity embed dialog.
   */
  public function testEntityEmbedDialog() {
    // Ensure that the route is not accessible without specifying all the
    // parameters.
    $this->DrupalGet($this->dialog_url);
    $this->assertResponse(404, 'Embed dialog is not accessible without specifying filter format and embed button.');
    $this->DrupalGet($this->dialog_url . 'custom_fromat');
    $this->assertResponse(404, 'Embed dialog is not accessible without specifying embed button.');

    // Ensure that the route is not accessible with an invalid embed button.
    $this->DrupalGet($this->dialog_url . 'custom_format/invalid-embed-button');
    $this->assertResponse(404, 'Embed dialog is not accessible without specifying filter format and embed button.');

    // Ensure that the route is accessible with a valid embed button.
    // 'Node' embed button is provided by default by the module and hence the
    // request must be successful.
    $this->DrupalGet($this->dialog_url . 'custom_format/node');
    $this->assertResponse(200, 'Embed dialog is accessible with correct filter format and embed button.');

    // Ensure form structure of the 'select' step and submit form.
    $this->assertFieldByName('attributes[data-entity-id]', '', 'Entity ID/UUID field is present.');
    $edit = array(
      'attributes[data-entity-id]' => $this->node->id(),
    );
    $this->drupalPostAjaxForm(NULL, $edit, array('op' => t('Next')));

    // Ensure form structure of the 'embed' step and submit form.
    $this->assertFieldByName('attributes[data-entity-embed-display]', 'Display plugin field is present.');
  }

}

I'm not sure what I'm doing wrong. Will be great if you could take a look.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

slashrsm’s picture

Issue tags: +Media Initiative, +sprint
slashrsm’s picture

Issue tags: +D8Media
Dave Reid’s picture

Ooh, I think I've finally figured out why this doesn't work!

This works with drupalPostAjaxForm():

      '#ajax' => array(
        'callback' => '::updateTypeSettings',
        'wrapper' => 'embed-type-settings-wrapper', // <-- Selector ID provided here.
        'effect' => 'fade',
      ),
...
  public function updateTypeSettings(array &$form, FormStateInterface $form_state) {
    $response = new AjaxResponse();

    // Update options for entity type bundles.
    $response->addCommand(new ReplaceCommand(
      NULL, // <-- Selector not provided here.
      $form['type_settings']
    ));

    return $response;
  }

This does not:

      '#ajax' => array(
        'callback' => '::updateTypeSettings',
         // <-- Selector not provided here.
        'effect' => 'fade',
      ),
...
  public function updateTypeSettings(array &$form, FormStateInterface $form_state) {
    $response = new AjaxResponse();

    // Update options for entity type bundles.
    $response->addCommand(new ReplaceCommand(
      '#embed-type-settings-wrapper', // <-- Selector ID provided here
      $form['type_settings']
    ));

    return $response;
  }

I think I need to file a bug in Drupal 8 in drupalProcessAjaxResponse()

Dave Reid’s picture

And here's the code in drupalProcessAjaxResponse that shows that they don't support arbitrary selectors in the AjaxResponse:

      switch ($command['command']) {
        case 'insert':
          $wrapperNode = NULL;
          // When a command doesn't specify a selector, use the
          // #ajax['wrapper'] which is always an HTML ID.
          if (!isset($command['selector'])) {
            $wrapperNode = $xpath->query('//*[@id="' . $ajax_settings['wrapper'] . '"]')->item(0);
          }
          // @todo Ajax commands can target any jQuery selector, but these are
          //   hard to fully emulate with XPath. For now, just handle 'head'
          //   and 'body', since these are used by
          //   \Drupal\Core\Ajax\AjaxResponse::ajaxRender().
          elseif (in_array($command['selector'], array('head', 'body'))) {
            $wrapperNode = $xpath->query('//' . $command['selector'])->item(0);
          }
          if ($wrapperNode) {
            ...
Dave Reid’s picture

It looks like BrowserTestBase should probably be used for complex AJAX testing: https://www.drupal.org/node/2232861. But it appears that it's not actually ready until Drupal 8.1. And lacks JavaScript so cannot actually be used for anything AJAX related: https://www.drupal.org/node/2469713.

Dave Reid’s picture

An example of a contrib module test converted to BrowserTestBase: https://www.drupal.org/node/2469609

Dave Reid’s picture

FYI you should just be able to override drupalProcessAjaxResponse() in your base test class that extends WebTestBase to include the patch.

Dave Reid’s picture

lokapujya’s picture

Status: Needs work » Needs review
FileSize
3.96 KB

Is this still worth fixing? I worked on adding a test. This is a copy of the replace test, but it can be modified to reproduce the issue. Will post this for now. This was meant for the core issue.

Status: Needs review » Needs work

The last submitted patch, 9: 2328659-9-test.patch, failed testing.

lokapujya’s picture

Status: Needs work » Needs review

.

slashrsm’s picture

Status: Needs review » Needs work

Please submit core patches in the core issue.

What is the plan here? Convert to BrowserTestBase?

Wim Leers’s picture

Title: Issue with drupalPostAjaxForm while writing test for entity_embed dialog » Convert all existing entity_embed tests from WebTestBase to BrowserTestBase
Priority: Normal » Critical
Status: Needs work » Active

Convert to BrowserTestBase?

At this point in the Drupal 8 cycle, where nearly all WebTestBase tests in core have been converted to BrowserTestBase, that seems the only sensible path forward. If we convert entity_embed's tests to use BrowserTestBase, it will also be ready for Drupal 9.

Rescoping.

phenaproxima’s picture

Status: Active » Needs review
Issue tags: -D8Media
FileSize
18.98 KB

This is a pretty sloppy patch, but it converts all the tests to BrowserTestBase (and, for the parts of EntityEmbedDialogTest which use the defunct drupalPostAjaxForm(), to WebDriverTestBase). This might also bring things back to green in HEAD, which would be damn good progress.

The tests definitely still need refinement and refactoring, but this is a good start.

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -sprint

Given that the test coverage is currently in less-than-ideal state, and is failing in HEAD, I'm ignoring any and all nits (since I'd have lots of them for the tests in HEAD too), and focusing on only the things that truly matter: the surface area covered by tests, and minimizing change.

  1. +++ b/tests/src/Functional/EntityEmbedDialogTest.php
    @@ -91,46 +91,6 @@ class EntityEmbedDialogTest extends EntityEmbedTestBase {
    -    $title = $this->node->getTitle() . ' (' . $this->node->id() . ')';
    

    Why can we delete all this? This means we reduce test coverage.

  2. +++ b/tests/src/FunctionalJavascript/EntityEmbedDialogTest.php
    @@ -0,0 +1,101 @@
    +  public function testEntityEmbedButtonMarkup() {
    

    Aha, we moved it here, because it needed JS to run those tests, since BrowserTestBase doesn't emulate the AJAXy stuff without executing JS like WebTestBase used to do.

  3. Everything else is trivial, a few things raise eyebrows, but nothing major.

This gets the job done; it gets tests to not only work using the non-deprecated test system, but also makes them pass again.

Wim Leers’s picture

phenaproxima’s picture

Wim Leers’s picture

Status: Reviewed & tested by the community » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.