Verified Commit 2a95e3fd authored by Lauri Timmanee's avatar Lauri Timmanee
Browse files

Issue #3316274 by Wim Leers, catch, nod_, alexpott, longwave, bnjmnm:...

Issue #3316274 by Wim Leers, catch, nod_, alexpott, longwave, bnjmnm: Stabilize FunctionalJavascript testing AJAX: add ::assertExpectedAjaxRequest()
parent 372878cf
Loading
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -40,7 +40,6 @@ public function testBlockAddThemeSelector() {
    $assert_session->selectExists('Theme')->selectOption('claro');
    $assert_session->assertWaitOnAjaxRequest();
    $assert_session->selectExists('Region')->selectOption('pre_content');
    $assert_session->assertWaitOnAjaxRequest();
    // Switch to a theme that doesn't contain the region selected above.
    $assert_session->selectExists('Theme')->selectOption('stark');
    $assert_session->assertWaitOnAjaxRequest();
+47 −31
Original line number Diff line number Diff line
@@ -34,10 +34,9 @@ public function testSettingsOnlyFireAjaxWithCkeditor5() {

    // Enable media embed to trigger an AJAX rebuild.
    $this->assertTrue($page->hasUncheckedField('filters[media_embed][status]'));
    $this->assertSame(0, $this->getAjaxResponseCount());
    $this->assertNoAjaxRequestTriggered();
    $page->checkField('filters[media_embed][status]');
    $assert_session->assertWaitOnAjaxRequest();
    $this->assertSame(1, $this->getAjaxResponseCount());
    $assert_session->assertExpectedAjaxRequest(1);

    // Perform the same steps as above with CKEditor, and confirm AJAX callbacks
    // are not triggered on settings changes.
@@ -47,25 +46,25 @@ public function testSettingsOnlyFireAjaxWithCkeditor5() {
    // trigger an AJAX rebuild.
    $this->assertTrue($page->hasUncheckedField('filters[media_embed][status]'));
    $page->checkField('filters[media_embed][status]');
    $this->assertSame(0, $this->getAjaxResponseCount());
    $this->assertNoAjaxRequestTriggered();

    // Confirm that AJAX updates happen when attempting to switch to CKEditor 5,
    // even if prevented from doing so by validation.
    $this->drupalGet('admin/config/content/formats/add');
    $page->fillField('name', 'trigger validator');
    $assert_session->waitForText('Machine name');
    $page->checkField('roles[authenticated]');
    $this->assertFalse($assert_session->elementExists('css', '#edit-name-machine-name-suffix')->isVisible());
    $name_field = $page->findField('name');
    $name_field->setValue('trigger validator');
    $this->assertTrue($assert_session->elementExists('css', '#edit-name-machine-name-suffix')->isVisible());

    // Enable a filter that is incompatible with CKEditor 5, so validation is
    // triggered when attempting to switch.
    $incompatible_filter_name = 'filters[filter_incompatible][status]';
    $this->assertTrue($page->hasUncheckedField($incompatible_filter_name));
    $page->checkField($incompatible_filter_name);
    $this->assertSame(0, $this->getAjaxResponseCount());
    $this->assertNoAjaxRequestTriggered();

    $page->selectFieldOption('editor[editor]', 'ckeditor5');
    $assert_session->assertWaitOnAjaxRequest();
    $this->assertSame(1, $this->getAjaxResponseCount());
    $assert_session->assertExpectedAjaxRequest(1);

    $filter_warning = 'CKEditor 5 only works with HTML-based text formats. The "A TYPE_MARKUP_LANGUAGE filter incompatible with CKEditor 5" (filter_incompatible) filter implies this text format is not HTML anymore.';

@@ -78,38 +77,61 @@ public function testSettingsOnlyFireAjaxWithCkeditor5() {
    // been corrected.
    $this->assertTrue($page->hasCheckedField($incompatible_filter_name));
    $page->uncheckField($incompatible_filter_name);
    $assert_session->assertWaitOnAjaxRequest();
    $this->assertSame(2, $this->getAjaxResponseCount());
    $assert_session->assertExpectedAjaxRequest(2);
    $assert_session->pageTextNotContains($filter_warning);
  }

  /**
   * Gets the Drupal AJAX response count observed on this page.
   * Asserts that no (new) AJAX requests were triggered.
   *
   * @return int
   *   The number of completed XHR requests observed since the page was loaded.
   * @param int $expected_cumulative_ajax_request_count
   *   The number of expected observed XHR requests since the page was loaded.
   */
  protected function getAjaxResponseCount(): int {
    // Half a second should suffice for any of the test's DOM interactions to
    // have triggered an AJAX request, if any.
  protected function assertNoAjaxRequestTriggered(int $expected_cumulative_ajax_request_count = 0): void {
    // In case of no requests triggered at all yet.
    if ($expected_cumulative_ajax_request_count === 0) {
      $result = $this->getSession()->evaluateScript(<<<JS
      (function() {
        return window.drupalCumulativeXhrCount;
      }())
JS);
      $this->assertSame(0, $result);
    }
    else {
      // In case of the non-first AJAX request, ensure that no AJAX requests are
      // in progress.
      try {
        $this->assertSession()->assertWaitOnAjaxRequest(500);
      }
      catch (\RuntimeException $e) {
      throw new \LogicException('An AJAX request was still being processed, this suggests a assertWaitOnAjaxRequest() call is missing.');
        throw new \LogicException(sprintf('This call to %s claims there no AJAX request was triggered, but this is wrong: %s.', __METHOD__, $e->getMessage()));
      }
      catch (\LogicException $e) {
        // This is the intent: ::assertWaitOnAjaxRequest() should detect an
        // "incorrect" call, because this assertion is asserting *no* AJAX
        // requests have been triggered.
        assert(str_contains($e->getMessage(), 'Unnecessary'));

        $result = $this->getSession()->evaluateScript(<<<JS
        (function() {
          return window.drupalCumulativeXhrCount;
        }())
JS);
        $this->assertSame($expected_cumulative_ajax_request_count, $result);
      }
    }

    // Now that there definitely is no more AJAX request in progress, count the
    // number of AJAX responses.
    // number of actual XHR requests, ensure they match.
    $javascript = <<<JS
(function(){
  return window.performance
    .getEntries()
    .filter(entry => entry.initiatorType === 'xmlhttprequest' && entry.name.indexOf('_wrapper_format=drupal_ajax') !== -1)
    .filter(entry => entry.initiatorType === 'xmlhttprequest')
    .length
})()
JS;
    return $this->getSession()->evaluateScript($javascript);
    $this->assertSame($expected_cumulative_ajax_request_count, $this->getSession()->evaluateScript($javascript));
  }

  /**
@@ -119,7 +141,6 @@ public function testUnavailableFiltersHiddenWhenSwitching() {
    $page = $this->getSession()->getPage();
    $assert_session = $this->assertSession();
    $this->createNewTextFormat($page, $assert_session, 'unicorn');
    $assert_session->assertWaitOnAjaxRequest();
    $assert_session->pageTextNotContains('Filter settings');

    // Switching to CKEditor 5 should keep the filter settings hidden.
@@ -137,8 +158,6 @@ public function testFilterCheckboxesToggleSettings() {

    $this->createNewTextFormat($page, $assert_session);

    $assert_session->assertWaitOnAjaxRequest();

    $media_tab = $page->find('css', '[href^="#edit-filters-media-embed-settings"]');
    $this->assertFalse($media_tab->isVisible(), 'Media filter settings should not be present because media filter is not enabled');

@@ -182,7 +201,6 @@ public function testMessagesDoNotAccumulate(): void {
    // validation error.
    $assert_session->waitForText('Source editing');
    $page->find('css', '[href^="#edit-editor-settings-plugins-ckeditor5-sourceediting"]')->click();
    $assert_session->assertWaitOnAjaxRequest();
    $assert_session->waitForText('Manually editable HTML tags');
    $source_edit_tags_field = $assert_session->fieldExists('editor[settings][plugins][ckeditor5_sourceEditing][allowed_tags]');
    $source_edit_tags_field->setValue('<strong>');
@@ -208,7 +226,6 @@ public function testPluginSettingsFormSection() {
    $assert_session = $this->assertSession();

    $this->createNewTextFormat($page, $assert_session);
    $assert_session->assertWaitOnAjaxRequest();

    // The default toolbar only enables the configurable heading plugin and the
    // non-configurable bold and italic plugins.
@@ -253,7 +270,6 @@ public function testLanguageConfigForm() {
    $assert_session = $this->assertSession();

    $this->createNewTextFormat($page, $assert_session);
    $assert_session->assertWaitOnAjaxRequest();

    // The language plugin config form should not be present.
    $assert_session->elementNotExists('css', '[data-drupal-selector="edit-editor-settings-plugins-ckeditor5-language"]');
+8 −20
Original line number Diff line number Diff line
@@ -63,13 +63,10 @@ public function testEnablingToVersion5Validation() {
    $filter_warning = 'CKEditor 5 only works with HTML-based text formats. The "A TYPE_MARKUP_LANGUAGE filter incompatible with CKEditor 5" (filter_incompatible) filter implies this text format is not HTML anymore.';

    $this->createNewTextFormat($page, $assert_session, 'unicorn');
    $page->selectFieldOption('editor[editor]', 'unicorn');
    $assert_session->assertWaitOnAjaxRequest();
    $page->checkField('filters[filter_html][status]');
    $page->checkField($incompatible_filter_name);
    $assert_session->assertWaitOnAjaxRequest();
    $page->selectFieldOption('editor[editor]', 'ckeditor5');
    $assert_session->assertWaitOnAjaxRequest();
    $assert_session->assertExpectedAjaxRequest(2);
    $assert_session->pageTextContains($filter_warning);

    // Disable the incompatible filter.
@@ -134,12 +131,10 @@ public function testSwitchToVersion5() {
    $assert_session = $this->assertSession();

    $this->createNewTextFormat($page, $assert_session, 'unicorn');
    $assert_session->assertWaitOnAjaxRequest();

    // Enable the HTML filter.
    $this->assertTrue($page->hasUncheckedField('filters[filter_html][status]'));
    $page->checkField('filters[filter_html][status]');
    $assert_session->assertWaitOnAjaxRequest();

    // Confirm the allowed HTML tags are the defaults initially.
    $this->assertHtmlEsqueFieldValueEquals('filters[filter_html][settings][allowed_html]', $this->defaultElementsWhenUpdatingNotCkeditor5);
@@ -150,7 +145,6 @@ public function testSwitchToVersion5() {
    // Return to the config form to confirm that switching text editors on
    // existing formats will properly switch allowed tags.
    $this->drupalGet('admin/config/content/formats/manage/unicorn');
    $assert_session->assertWaitOnAjaxRequest();
    $this->assertHtmlEsqueFieldValueEquals('filters[filter_html][settings][allowed_html]', $this->defaultElementsWhenUpdatingNotCkeditor5);

    $page->selectFieldOption('editor[editor]', 'ckeditor5');
@@ -171,7 +165,6 @@ public function testImgAddedViaUploadPlugin() {
    $assert_session = $this->assertSession();

    $this->createNewTextFormat($page, $assert_session);
    $assert_session->assertWaitOnAjaxRequest();

    $allowed_html_field = $assert_session->fieldExists('filters[filter_html][settings][allowed_html]');
    $this->assertTrue($allowed_html_field->hasAttribute('readonly'));
@@ -244,7 +237,6 @@ public function testAllowedTags() {
    $assert_session = $this->assertSession();

    $this->createNewTextFormat($page, $assert_session);
    $assert_session->assertWaitOnAjaxRequest();

    // Confirm the "allowed tags" field is  read only, and the value
    // matches the tags required by CKEditor.
@@ -262,7 +254,6 @@ public function testAllowedTags() {
    $this->assertSame($this->allowedElements, FilterFormat::load('ckeditor5')->filters('filter_html')->getConfiguration()['settings']['allowed_html']);

    $page->find('css', '[data-drupal-selector="edit-formats-ckeditor5"]')->clickLink('Configure');
    $assert_session->assertWaitOnAjaxRequest();

    // Add the block quote plugin to the CKEditor 5 toolbar.
    $this->assertNotEmpty($assert_session->waitForElement('css', '.ckeditor5-toolbar-item-blockQuote'));
@@ -290,7 +281,7 @@ public function testAllowedTags() {
    $this->assertHtmlEsqueFieldValueEquals('filters[filter_html][settings][allowed_html]', $allowed_with_blockquote);

    // Add the source editing plugin to the CKEditor 5 toolbar.
    $this->assertNotEmpty($assert_session->waitForElement('css', '.ckeditor5-toolbar-item-sourceEditing'));
    $this->assertNotEmpty($assert_session->waitForElement('css', '.ckeditor5-toolbar-available .ckeditor5-toolbar-item-sourceEditing'));
    $this->triggerKeyUp('.ckeditor5-toolbar-item-sourceEditing', 'ArrowDown');
    $assert_session->assertWaitOnAjaxRequest();

@@ -298,7 +289,6 @@ public function testAllowedTags() {
    // filter_html to include those additional tags.
    $assert_session->waitForText('Source editing');
    $page->find('css', '[href^="#edit-editor-settings-plugins-ckeditor5-sourceediting"]')->click();
    $assert_session->assertWaitOnAjaxRequest();
    $assert_session->waitForText('Manually editable HTML tags');
    $source_edit_tags_field = $assert_session->fieldExists('editor[settings][plugins][ckeditor5_sourceEditing][allowed_tags]');
    $source_edit_tags_field->setValue('<aside>');
@@ -379,22 +369,20 @@ public function testMediaElementAllowedTags() {
    $this->assertTrue($page->hasUncheckedField('filters[media_embed][status]'));
    $this->assertNull($assert_session->waitForElementVisible('css', '[data-drupal-selector=edit-filters-media-embed-settings]', 0));
    $page->checkField('filters[media_embed][status]');
    $assert_session->assertWaitOnAjaxRequest();
    $assert_session->assertExpectedAjaxRequest(2);
    $this->assertNotNull($assert_session->waitForElementVisible('css', '[data-drupal-selector=edit-filters-media-embed-settings]', 0));

    $page->clickLink('Embed media');
    $assert_session->assertWaitOnAjaxRequest();
    $page->checkField('filters[media_embed][settings][allowed_view_modes][view_mode_1]');
    $page->checkField('filters[media_embed][settings][allowed_view_modes][view_mode_2]');

    $allowed_with_media = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode>';
    $allowed_with_media_without_view_mode = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt>';
    $page->clickLink('Media');
    $assert_session->waitForText('Allow the user to override the default view mode');
    $this->assertTrue($page->hasUncheckedField('editor[settings][plugins][media_media][allow_view_mode_override]'));
    $this->assertHtmlEsqueFieldValueEquals('filters[filter_html][settings][allowed_html]', $allowed_with_media_without_view_mode);
    $page->checkField('editor[settings][plugins][media_media][allow_view_mode_override]');
    $assert_session->assertWaitOnAjaxRequest();
    $assert_session->assertExpectedAjaxRequest(3);
    $this->assertHtmlEsqueFieldValueEquals('filters[filter_html][settings][allowed_html]', $allowed_with_media);
    $this->saveNewTextFormat($page, $assert_session);
    $assert_session->pageTextContains('Added text format ckeditor5.');
@@ -412,14 +400,14 @@ public function testMediaElementAllowedTags() {
    // Ensure that data-align attribute is added to <drupal-media> when
    // filter_align is enabled.
    $page->checkField('filters[filter_align][status]');
    $assert_session->assertWaitOnAjaxRequest();
    $assert_session->assertExpectedAjaxRequest(1);
    $this->assertEquals($this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode data-align>', $allowed_html_field->getValue());

    // Disable media embed.
    $this->assertTrue($page->hasCheckedField('filters[media_embed][status]'));
    $page->uncheckField('filters[media_embed][status]');
    $assert_session->assertWaitOnAjaxRequest();

    $assert_session->assertExpectedAjaxRequest(2);
    // Confirm allowed tags no longer has <drupal-media>.
    $this->assertHtmlEsqueFieldValueEquals('filters[filter_html][settings][allowed_html]', $this->allowedElements);
  }
@@ -490,8 +478,8 @@ public function testFullHtml() {
    $page->checkField('roles[authenticated]');
    $page->selectFieldOption('editor[editor]', 'ckeditor5');
    $assert_session->assertWaitOnAjaxRequest();
    $this->assertNotEmpty($assert_session->waitForElement('css', '.ckeditor5-toolbar-item-link'));
    $this->triggerKeyUp('.ckeditor5-toolbar-item-link', 'ArrowDown');
    $this->assertNotEmpty($assert_session->waitForElement('css', '.ckeditor5-toolbar-available .ckeditor5-toolbar-item-underline'));
    $this->triggerKeyUp('.ckeditor5-toolbar-item-underline', 'ArrowDown');
    $assert_session->assertWaitOnAjaxRequest();
    $page->pressButton('Save configuration');
    $this->assertTrue($assert_session->waitForText('The text format Basic HTML has been updated.'));
+5 −3
Original line number Diff line number Diff line
@@ -295,8 +295,11 @@ public function languageOfPartsPluginConfigureLanguageListHelper($page, $assert_
    // Set correct value.
    $vertical_tab_link = $page->find('xpath', "//ul[contains(@class, 'vertical-tabs__menu')]/li/a[starts-with(@href, '#edit-editor-settings-plugins-ckeditor5-language')]");
    $vertical_tab_link->click();
    $page->selectFieldOption('editor[settings][plugins][ckeditor5_language][language_list]', $option);
    $select = $page->findField('editor[settings][plugins][ckeditor5_language][language_list]');
    if ($select->getValue() !== $option) {
      $select->selectOption($option);
      $assert_session->assertWaitOnAjaxRequest();
    }
    $page->pressButton('Save configuration');
    $assert_session->responseContains('The text format <em class="placeholder">ckeditor5</em> has been updated.');
  }
@@ -400,7 +403,6 @@ public function testActiveTabsMaintained() {
    $assert_session = $this->assertSession();

    $this->createNewTextFormat($page, $assert_session);
    $assert_session->assertWaitOnAjaxRequest();

    // Initial vertical tabs: 3 for filters, 1 for CKE5 plugins.
    $this->assertSame([
+1 −2
Original line number Diff line number Diff line
@@ -66,20 +66,19 @@ public function createNewTextFormat($page, $assert_session, $name = 'ckeditor5')
      // before CKEditor 5 can be enabled.
      $this->assertTrue($page->hasUncheckedField('filters[filter_html][status]'));
      $page->checkField('filters[filter_html][status]');
      $assert_session->assertWaitOnAjaxRequest();

      // Add the tags that must be included in the html filter for CKEditor 5.
      $allowed_html_field = $assert_session->fieldExists('filters[filter_html][settings][allowed_html]');
      $allowed_html_field->setValue('<p> <br>');
    }
    $page->selectFieldOption('editor[editor]', $name);
    $assert_session->assertExpectedAjaxRequest(1);
  }

  /**
   * Save the new text format.
   */
  public function saveNewTextFormat($page, $assert_session) {
    $assert_session->assertWaitOnAjaxRequest();
    $page->pressButton('Save configuration');
    $this->assertTrue($assert_session->waitForText('Added text format'), "Confirm new text format saved");
  }
Loading