Update Composer, update everything

This commit is contained in:
Oliver Davies 2018-11-23 12:29:20 +00:00
parent ea3e94409f
commit dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions

View file

@ -0,0 +1,44 @@
<?php
namespace Drupal\FunctionalJavascriptTests\Ajax;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Tests Ajax callbacks on FAPI elements.
*
* @group Ajax
*/
class AjaxCallbacksTest extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['ajax_forms_test'];
/**
* Tests if Ajax callback works on date element.
*/
public function testDateAjaxCallback() {
// Test Ajax callback when date changes.
$this->drupalGet('ajax_forms_test_ajax_element_form');
$this->assertNotEmpty($this->getSession()->getPage()->find('xpath', '//div[@id="ajax_date_value"][text()="No date yet selected"]'));
$this->getSession()->executeScript('jQuery("[data-drupal-selector=edit-date]").val("2016-01-01").trigger("change");');
$this->assertNotEmpty($this->assertSession()->waitForElement('xpath', '//div[@id="ajax_date_value"]/div[text()="2016-01-01"]'));
}
/**
* Tests if Ajax callback works on datetime element.
*/
public function testDateTimeAjaxCallback() {
// Test Ajax callback when datetime changes.
$this->drupalGet('ajax_forms_test_ajax_element_form');
$this->assertNotEmpty($this->getSession()->getPage()->find('xpath', '//div[@id="ajax_datetime_value"][text()="No datetime selected."]'));
$this->getSession()->executeScript('jQuery("[data-drupal-selector=edit-datetime-date]").val("2016-01-01");');
$this->getSession()->executeScript('jQuery("[data-drupal-selector=edit-datetime-time]").val("12:00:00").trigger("change");');
$this->assertNotEmpty($this->assertSession()->waitForElement('xpath', '//div[@id="ajax_datetime_value"]/div[text()="2016-01-01 12:00:00"]'));
}
}

View file

@ -0,0 +1,108 @@
<?php
namespace Drupal\FunctionalJavascriptTests\Ajax;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Url;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Tests the usage of form caching for AJAX forms.
*
* @group Ajax
*/
class AjaxFormCacheTest extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['ajax_test', 'ajax_forms_test'];
/**
* Tests the usage of form cache for AJAX forms.
*/
public function testFormCacheUsage() {
/** @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $key_value_expirable */
$key_value_expirable = \Drupal::service('keyvalue.expirable')->get('form');
$this->drupalLogin($this->rootUser);
// Ensure that the cache is empty.
$this->assertEqual(0, count($key_value_expirable->getAll()));
// Visit an AJAX form that is not cached, 3 times.
$uncached_form_url = Url::fromRoute('ajax_forms_test.commands_form');
$this->drupalGet($uncached_form_url);
$this->drupalGet($uncached_form_url);
$this->drupalGet($uncached_form_url);
// The number of cache entries should not have changed.
$this->assertEqual(0, count($key_value_expirable->getAll()));
}
/**
* Tests AJAX forms in blocks.
*/
public function testBlockForms() {
$this->container->get('module_installer')->install(['block', 'search']);
$this->rebuildContainer();
$this->container->get('router.builder')->rebuild();
$this->drupalLogin($this->rootUser);
$this->drupalPlaceBlock('search_form_block', ['weight' => -5]);
$this->drupalPlaceBlock('ajax_forms_test_block');
$this->drupalGet('');
$session = $this->getSession();
// Select first option and trigger ajax update.
$session->getPage()->selectFieldOption('edit-test1', 'option1');
// DOM update: The InsertCommand in the AJAX response changes the text
// in the option element to 'Option1!!!'.
$opt1_selector = $this->assertSession()->waitForElement('css', "select[data-drupal-selector='edit-test1'] option:contains('Option 1!!!')");
$this->assertNotEmpty($opt1_selector);
$this->assertTrue($opt1_selector->isSelected());
// Confirm option 3 exists.
$page = $session->getPage();
$opt3_selector = $page->find('xpath', '//select[@data-drupal-selector="edit-test1"]//option[@value="option3"]');
$this->assertNotEmpty($opt3_selector);
// Confirm success message appears after a submit.
$page->findButton('edit-submit')->click();
$this->assertSession()->waitForButton('edit-submit');
$updated_page = $session->getPage();
$updated_page->hasContent('Submission successful.');
}
/**
* Tests AJAX forms on pages with a query string.
*/
public function testQueryString() {
$this->container->get('module_installer')->install(['block']);
$this->drupalLogin($this->rootUser);
$this->drupalPlaceBlock('ajax_forms_test_block');
$url = Url::fromRoute('entity.user.canonical', ['user' => $this->rootUser->id()], ['query' => ['foo' => 'bar']]);
$this->drupalGet($url);
$session = $this->getSession();
// Select first option and trigger ajax update.
$session->getPage()->selectFieldOption('edit-test1', 'option1');
// DOM update: The InsertCommand in the AJAX response changes the text
// in the option element to 'Option1!!!'.
$opt1_selector = $this->assertSession()->waitForElement('css', "option:contains('Option 1!!!')");
$this->assertNotEmpty($opt1_selector);
$url->setOption('query', [
'foo' => 'bar',
FormBuilderInterface::AJAX_FORM_REQUEST => 1,
MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax',
]);
$this->assertUrl($url);
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace Drupal\FunctionalJavascriptTests\Ajax;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Tests the Ajax image buttons work with key press events.
*
* @group Ajax
*/
class AjaxFormImageButtonTest extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['ajax_forms_test'];
/**
* Tests image buttons can be operated with the keyboard ENTER key.
*/
public function testAjaxImageButton() {
// Get a Field UI manage-display page.
$this->drupalGet('ajax_forms_image_button_form');
$assertSession = $this->assertSession();
$session = $this->getSession();
$enter_key_event = <<<JS
jQuery('#edit-image-button')
.trigger(jQuery.Event('keypress', {
which: 13
}));
JS;
// PhantomJS driver has buggy behavior with key events, we send a JavaScript
// key event instead.
// @todo: use WebDriver event when we remove PhantomJS driver.
$session->executeScript($enter_key_event);
$this->assertNotEmpty($assertSession->waitForElementVisible('css', '#ajax-1-more-div'), 'Page updated after image button pressed');
}
}

View file

@ -2,14 +2,14 @@
namespace Drupal\FunctionalJavascriptTests\Ajax;
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Performs tests on AJAX forms in cached pages.
*
* @group Ajax
*/
class AjaxFormPageCacheTest extends JavascriptTestBase {
class AjaxFormPageCacheTest extends WebDriverTestBase {
/**
* {@inheritdoc}
@ -41,7 +41,6 @@ class AjaxFormPageCacheTest extends JavascriptTestBase {
*/
public function testSimpleAJAXFormValue() {
$this->drupalGet('ajax_forms_test_get_form');
$this->assertEquals($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
$build_id_initial = $this->getFormBuildId();
// Changing the value of a select input element, triggers a AJAX
@ -55,8 +54,8 @@ class AjaxFormPageCacheTest extends JavascriptTestBase {
// Wait for the DOM to update. The HtmlCommand will update
// #ajax_selected_color to reflect the color change.
$green_div = $this->assertSession()->waitForElement('css', "#ajax_selected_color div:contains('green')");
$this->assertNotNull($green_div, 'DOM update: The selected color DIV is green.');
$green_span = $this->assertSession()->waitForElement('css', "#ajax_selected_color:contains('green')");
$this->assertNotNull($green_span, 'DOM update: The selected color SPAN is green.');
// Confirm the operation of the UpdateBuildIdCommand.
$build_id_first_ajax = $this->getFormBuildId();
@ -67,8 +66,8 @@ class AjaxFormPageCacheTest extends JavascriptTestBase {
$session->getPage()->selectFieldOption('select', 'red');
// Wait for the DOM to update.
$red_div = $this->assertSession()->waitForElement('css', "#ajax_selected_color div:contains('red')");
$this->assertNotNull($red_div, 'DOM update: The selected color DIV is red.');
$red_span = $this->assertSession()->waitForElement('css', "#ajax_selected_color:contains('red')");
$this->assertNotNull($red_span, 'DOM update: The selected color SPAN is red.');
// Confirm the operation of the UpdateBuildIdCommand.
$build_id_second_ajax = $this->getFormBuildId();
@ -77,7 +76,6 @@ class AjaxFormPageCacheTest extends JavascriptTestBase {
// Emulate a push of the reload button and then repeat the test sequence
// this time with a page loaded from the cache.
$session->reload();
$this->assertEquals($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$build_id_from_cache_initial = $this->getFormBuildId();
$this->assertEquals($build_id_initial, $build_id_from_cache_initial, 'Build id is the same as on the first request');
@ -86,8 +84,8 @@ class AjaxFormPageCacheTest extends JavascriptTestBase {
$session->getPage()->selectFieldOption('select', 'green');
// Wait for the DOM to update.
$green_div2 = $this->assertSession()->waitForElement('css', "#ajax_selected_color div:contains('green')");
$this->assertNotNull($green_div2, 'DOM update: After reload - the selected color DIV is green.');
$green_span2 = $this->assertSession()->waitForElement('css', "#ajax_selected_color:contains('green')");
$this->assertNotNull($green_span2, 'DOM update: After reload - the selected color SPAN is green.');
$build_id_from_cache_first_ajax = $this->getFormBuildId();
$this->assertNotEquals($build_id_from_cache_initial, $build_id_from_cache_first_ajax, 'Build id is changed in the simpletest-DOM on first AJAX submission');
@ -98,8 +96,8 @@ class AjaxFormPageCacheTest extends JavascriptTestBase {
$session->getPage()->selectFieldOption('select', 'red');
// Wait for the DOM to update.
$red_div2 = $this->assertSession()->waitForElement('css', "#ajax_selected_color div:contains('red')");
$this->assertNotNull($red_div2, 'DOM update: After reload - the selected color DIV is red.');
$red_span2 = $this->assertSession()->waitForElement('css', "#ajax_selected_color:contains('red')");
$this->assertNotNull($red_span2, 'DOM update: After reload - the selected color SPAN is red.');
$build_id_from_cache_second_ajax = $this->getFormBuildId();
$this->assertNotEquals($build_id_from_cache_first_ajax, $build_id_from_cache_second_ajax, 'Build id changes on subsequent AJAX submissions');
@ -115,7 +113,9 @@ class AjaxFormPageCacheTest extends JavascriptTestBase {
$this->drupalGet('ajax_validation_test');
// Changing the value of the textfield will trigger an AJAX
// request/response.
$this->getSession()->getPage()->fillField('drivertext', 'some dumb text');
$field = $this->getSession()->getPage()->findField('drivertext');
$field->setValue('some dumb text');
$field->blur();
// When the AJAX command updates the DOM a <ul> unsorted list
// "message__list" structure will appear on the page echoing back the

View file

@ -0,0 +1,57 @@
<?php
namespace Drupal\FunctionalJavascriptTests\Ajax;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Tests that form elements in groups work correctly with AJAX.
*
* @group Ajax
*/
class AjaxInGroupTest extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['ajax_forms_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalLogin($this->drupalCreateUser(['access content']));
}
/**
* Submits forms with select and checkbox elements via Ajax.
*/
public function testSimpleAjaxFormValue() {
$this->drupalGet('/ajax_forms_test_get_form');
$assert_session = $this->assertSession();
$assert_session->responseContains('Test group');
$assert_session->responseContains('AJAX checkbox in a group');
$session = $this->getSession();
$checkbox_original = $session->getPage()->findField('checkbox_in_group');
$this->assertNotNull($checkbox_original, 'The checkbox_in_group is on the page.');
$original_id = $checkbox_original->getAttribute('id');
// Triggers a AJAX request/response.
$checkbox_original->check();
// The response contains a new nested "test group" form element, similar
// to the one already in the DOM except for a change in the form build id.
$checkbox_new = $assert_session->waitForElement('xpath', "//input[@name='checkbox_in_group' and not(@id='$original_id')]");
$this->assertNotNull($checkbox_new, 'DOM update: clicking the checkbox refreshed the checkbox_in_group structure');
$assert_session->responseContains('Test group');
$assert_session->responseContains('AJAX checkbox in a group');
$assert_session->responseContains('AJAX checkbox in a nested group');
$assert_session->responseContains('Another AJAX checkbox in a nested group');
}
}

View file

@ -2,14 +2,14 @@
namespace Drupal\FunctionalJavascriptTests\Ajax;
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Tests AJAX responses.
*
* @group Ajax
*/
class AjaxTest extends JavascriptTestBase {
class AjaxTest extends WebDriverTestBase {
/**
* {@inheritdoc}
@ -82,4 +82,119 @@ class AjaxTest extends JavascriptTestBase {
$this->assertNotContains($fake_library, $libraries);
}
/**
* Tests that various AJAX responses with DOM elements are correctly inserted.
*
* After inserting DOM elements, Drupal JavaScript behaviors should be
* reattached and all top-level elements of type Node.ELEMENT_NODE need to be
* part of the context.
*/
public function testInsertAjaxResponse() {
$render_single_root = [
'pre-wrapped-div' => '<div class="pre-wrapped">pre-wrapped<script> var test;</script></div>',
'pre-wrapped-span' => '<span class="pre-wrapped">pre-wrapped<script> var test;</script></span>',
'pre-wrapped-whitespace' => ' <div class="pre-wrapped-whitespace">pre-wrapped-whitespace</div>' . "\n",
'not-wrapped' => 'not-wrapped',
'comment-string-not-wrapped' => '<!-- COMMENT -->comment-string-not-wrapped',
'comment-not-wrapped' => '<!-- COMMENT --><div class="comment-not-wrapped">comment-not-wrapped</div>',
'svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10"><rect x="0" y="0" height="10" width="10" fill="green"/></svg>',
'empty' => '',
];
$render_multiple_root_unwrapper = [
'mixed' => ' foo <!-- COMMENT --> foo bar<div class="a class"><p>some string</p></div> additional not wrapped strings, <!-- ANOTHER COMMENT --> <p>final string</p>',
'top-level-only' => '<div>element #1</div><div>element #2</div>',
'top-level-only-pre-whitespace' => ' <div>element #1</div><div>element #2</div> ',
'top-level-only-middle-whitespace-span' => '<span>element #1</span> <span>element #2</span>',
'top-level-only-middle-whitespace-div' => '<div>element #1</div> <div>element #2</div>',
];
// This is temporary behavior for BC reason.
$render_multiple_root_wrapper = [];
foreach ($render_multiple_root_unwrapper as $key => $render) {
$render_multiple_root_wrapper["$key--effect"] = '<div>' . $render . '</div>';
}
$expected_renders = array_merge(
$render_single_root,
$render_multiple_root_wrapper,
$render_multiple_root_unwrapper
);
// Checking default process of wrapping Ajax content.
foreach ($expected_renders as $render_type => $expected) {
$this->assertInsert($render_type, $expected);
}
// Checking custom ajaxWrapperMultipleRootElements wrapping.
$custom_wrapper_multiple_root = <<<JS
(function($, Drupal){
Drupal.theme.ajaxWrapperMultipleRootElements = function (elements) {
return $('<div class="my-favorite-div"></div>').append(elements);
};
}(jQuery, Drupal));
JS;
$expected = '<div class="my-favorite-div"><span>element #1</span> <span>element #2</span></div>';
$this->assertInsert('top-level-only-middle-whitespace-span--effect', $expected, $custom_wrapper_multiple_root);
// Checking custom ajaxWrapperNewContent wrapping.
$custom_wrapper_new_content = <<<JS
(function($, Drupal){
Drupal.theme.ajaxWrapperNewContent = function (elements) {
return $('<div class="div-wrapper-forever"></div>').append(elements);
};
}(jQuery, Drupal));
JS;
$expected = '<div class="div-wrapper-forever"></div>';
$this->assertInsert('empty', $expected, $custom_wrapper_new_content);
}
/**
* Assert insert.
*
* @param string $render_type
* Render type.
* @param string $expected
* Expected result.
* @param string $script
* Script for additional theming.
*/
public function assertInsert($render_type, $expected, $script = '') {
// Check insert to block element.
$this->drupalGet('ajax-test/insert-block-wrapper');
$this->getSession()->executeScript($script);
$this->clickLink("Link html $render_type");
$this->assertWaitPageContains('<div class="ajax-target-wrapper"><div id="ajax-target">' . $expected . '</div></div>');
$this->drupalGet('ajax-test/insert-block-wrapper');
$this->getSession()->executeScript($script);
$this->clickLink("Link replaceWith $render_type");
$this->assertWaitPageContains('<div class="ajax-target-wrapper">' . $expected . '</div>');
// Check insert to inline element.
$this->drupalGet('ajax-test/insert-inline-wrapper');
$this->getSession()->executeScript($script);
$this->clickLink("Link html $render_type");
$this->assertWaitPageContains('<div class="ajax-target-wrapper"><span id="ajax-target-inline">' . $expected . '</span></div>');
$this->drupalGet('ajax-test/insert-inline-wrapper');
$this->getSession()->executeScript($script);
$this->clickLink("Link replaceWith $render_type");
$this->assertWaitPageContains('<div class="ajax-target-wrapper">' . $expected . '</div>');
}
/**
* Asserts that page contains an expected value after waiting.
*
* @param string $expected
* A needle text.
*/
protected function assertWaitPageContains($expected) {
$page = $this->getSession()->getPage();
$this->assertTrue($page->waitFor(10, function () use ($page, $expected) {
// Clear content from empty styles and "processed" classes after effect.
$content = str_replace([' class="processed"', ' processed', ' style=""'], '', $page->getContent());
return stripos($content, $expected) !== FALSE;
}), "Page contains expected value: $expected");
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\FunctionalJavascriptTests\Ajax;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Tests the compatibility of the ajax.es6.js file.
*
* @group Ajax
*/
class BackwardCompatibilityTest extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'js_ajax_test',
];
/**
* Ensures Drupal.Ajax.element_settings BC layer.
*/
public function testAjaxBackwardCompatibility() {
$this->drupalGet('/js_ajax_test');
$this->click('#edit-test-button');
$this->assertSession()
->waitForElement('css', '#js_ajax_test_form_element');
$elements = $this->cssSelect('#js_ajax_test_form_element');
$this->assertCount(1, $elements);
$json = $elements[0]->getText();
$data = json_decode($json, TRUE);
$this->assertEquals([
'element_settings' => 'catbro',
'elementSettings' => 'catbro',
], $data);
}
}

View file

@ -0,0 +1,130 @@
<?php
namespace Drupal\FunctionalJavascriptTests\Ajax;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Performs tests on AJAX framework commands.
*
* @group Ajax
*/
class CommandsTest extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'ajax_test', 'ajax_forms_test'];
/**
* Tests the various Ajax Commands.
*/
public function testAjaxCommands() {
$session = $this->getSession();
$page = $this->getSession()->getPage();
$form_path = 'ajax_forms_test_ajax_commands_form';
$web_user = $this->drupalCreateUser(['access content']);
$this->drupalLogin($web_user);
$this->drupalGet($form_path);
// Tests the 'add_css' command.
$page->pressButton("AJAX 'add_css' command");
$this->assertWaitPageContains('my/file.css');
// Tests the 'after' command.
$page->pressButton("AJAX 'After': Click to put something after the div");
$this->assertWaitPageContains('<div id="after_div">Something can be inserted after this</div>This will be placed after');
// Tests the 'alert' command.
$test_alert_command = <<<JS
window.alert = function() {
document.body.innerHTML += '<div class="alert-command">Alert</div>';
};
JS;
$session->executeScript($test_alert_command);
$page->pressButton("AJAX 'Alert': Click to alert");
$this->assertWaitPageContains('<div class="alert-command">Alert</div>');
// Tests the 'append' command.
$page->pressButton("AJAX 'Append': Click to append something");
$this->assertWaitPageContains('<div id="append_div">Append inside this divAppended text</div>');
// Tests the 'before' command.
$page->pressButton("AJAX 'before': Click to put something before the div");
$this->assertWaitPageContains('Before text<div id="before_div">Insert something before this.</div>');
// Tests the 'changed' command.
$page->pressButton("AJAX changed: Click to mark div changed.");
$this->assertWaitPageContains('<div id="changed_div" class="ajax-changed">');
// Tests the 'changed' command using the second argument.
// Refresh page for testing 'changed' command to same element again.
$this->drupalGet($form_path);
$page->pressButton("AJAX changed: Click to mark div changed with asterisk.");
$this->assertWaitPageContains('<div id="changed_div" class="ajax-changed"> <div id="changed_div_mark_this">This div can be marked as changed or not. <abbr class="ajax-changed" title="Changed">*</abbr> </div></div>');
// Tests the 'css' command.
$page->pressButton("Set the '#box' div to be blue.");
$this->assertWaitPageContains('<div id="css_div" style="background-color: blue;">');
// Tests the 'data' command.
$page->pressButton("AJAX data command: Issue command.");
$this->assertTrue($page->waitFor(10, function () use ($session) {
return 'testvalue' === $session->evaluateScript('window.jQuery("#data_div").data("testkey")');
}));
// Tests the 'html' command.
$page->pressButton("AJAX html: Replace the HTML in a selector.");
$this->assertWaitPageContains('<div id="html_div">replacement text</div>');
// Tests the 'insert' command.
$page->pressButton("AJAX insert: Let client insert based on #ajax['method'].");
$this->assertWaitPageContains('<div id="insert_div">insert replacement textOriginal contents</div>');
// Tests the 'invoke' command.
$page->pressButton("AJAX invoke command: Invoke addClass() method.");
$this->assertWaitPageContains('<div id="invoke_div" class="error">Original contents</div>');
// Tests the 'prepend' command.
$page->pressButton("AJAX 'prepend': Click to prepend something");
$this->assertWaitPageContains('<div id="prepend_div">prepended textSomething will be prepended to this div. </div>');
// Tests the 'remove' command.
$page->pressButton("AJAX 'remove': Click to remove text");
$this->assertWaitPageContains('<div id="remove_div"></div>');
// Tests the 'restripe' command.
$page->pressButton("AJAX 'restripe' command");
$this->assertWaitPageContains('<tr id="table-first" class="odd"><td>first row</td></tr>');
$this->assertWaitPageContains('<tr class="even"><td>second row</td></tr>');
// Tests the 'settings' command.
$test_settings_command = <<<JS
Drupal.behaviors.testSettingsCommand = {
attach: function (context, settings) {
window.jQuery('body').append('<div class="test-settings-command">' + settings.ajax_forms_test.foo + '</div>');
}
};
JS;
$session->executeScript($test_settings_command);
// @todo: Replace after https://www.drupal.org/project/drupal/issues/2616184
$session->executeScript('window.jQuery("#edit-settings-command-example").mousedown();');
$this->assertWaitPageContains('<div class="test-settings-command">42</div>');
}
/**
* Asserts that page contains a text after waiting.
*
* @param string $text
* A needle text.
*/
protected function assertWaitPageContains($text) {
$page = $this->getSession()->getPage();
$page->waitFor(10, function () use ($page, $text) {
return stripos($page->getContent(), $text) !== FALSE;
});
$this->assertContains($text, $page->getContent());
}
}

View file

@ -0,0 +1,167 @@
<?php
namespace Drupal\FunctionalJavascriptTests\Ajax;
use Drupal\ajax_test\Controller\AjaxTestController;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Performs tests on opening and manipulating dialogs via AJAX commands.
*
* @group Ajax
*/
class DialogTest extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['ajax_test', 'ajax_forms_test', 'contact'];
/**
* Test sending non-JS and AJAX requests to open and manipulate modals.
*/
public function testDialog() {
$this->drupalLogin($this->drupalCreateUser(['administer contact forms']));
// Ensure the elements render without notices or exceptions.
$this->drupalGet('ajax-test/dialog');
// Set up variables for this test.
$dialog_renderable = AjaxTestController::dialogContents();
$dialog_contents = \Drupal::service('renderer')->renderRoot($dialog_renderable);
// Check that requesting a modal dialog without JS goes to a page.
$this->drupalGet('ajax-test/dialog-contents');
$this->assertSession()->responseContains($dialog_contents);
// Visit the page containing the many test dialog links.
$this->drupalGet('ajax-test/dialog');
// Tests a basic modal dialog by verifying the contents of the dialog are as
// expected.
$this->getSession()->getPage()->clickLink('Link 1 (modal)');
// Clicking the link triggers a AJAX request/response.
// Opens a Dialog panel.
$link1_dialog_div = $this->assertSession()->waitForElementVisible('css', 'div.ui-dialog');
$this->assertNotNull($link1_dialog_div, 'Link was used to open a dialog ( modal )');
$link1_modal = $link1_dialog_div->find('css', '#drupal-modal');
$this->assertNotNull($link1_modal, 'Link was used to open a dialog ( non-modal )');
$this->assertSession()->responseContains($dialog_contents);
$dialog_title = $link1_dialog_div->find('css', "span.ui-dialog-title:contains('AJAX Dialog & contents')");
$this->assertNotNull($dialog_title);
$dialog_title_amp = $link1_dialog_div->find('css', "span.ui-dialog-title:contains('AJAX Dialog &amp; contents')");
$this->assertNull($dialog_title_amp);
// Close open dialog, return to the dialog links page.
$close_button = $link1_dialog_div->findButton('Close');
$this->assertNotNull($close_button);
$close_button->press();
// Tests a modal with a dialog-option.
// Link 2 is similar to Link 1, except it submits additional width
// information which must be echoed in the resulting DOM update.
$this->getSession()->getPage()->clickLink('Link 2 (modal)');
$dialog = $this->assertSession()->waitForElementVisible('css', 'div.ui-dialog');
$this->assertNotNull($dialog, 'Link was used to open a dialog ( non-modal, with options )');
$style = $dialog->getAttribute('style');
$this->assertContains('width: 400px;', $style, new FormattableMarkup('Modal respected the dialog-options width parameter. Style = style', ['%style' => $style]));
// Reset: Return to the dialog links page.
$this->drupalGet('ajax-test/dialog');
// Test a non-modal dialog ( with target ).
$this->clickLink('Link 3 (non-modal)');
$non_modal_dialog = $this->assertSession()->waitForElementVisible('css', 'div.ui-dialog');
$this->assertNotNull($non_modal_dialog, 'Link opens a non-modal dialog.');
// Tests the dialog contains a target element specified in the AJAX request.
$non_modal_dialog->find('css', 'div#ajax-test-dialog-wrapper-1');
$this->assertSession()->responseContains($dialog_contents);
// Reset: Return to the dialog links page.
$this->drupalGet('ajax-test/dialog');
// Tests a non-modal dialog ( without target ).
$this->clickLink('Link 7 (non-modal, no target)');
$no_target_dialog = $this->assertSession()->waitForElementVisible('css', 'div.ui-dialog');
$this->assertNotNull($no_target_dialog, 'Link opens a non-modal dialog.');
$contents_no_target = $no_target_dialog->find('css', 'div.ui-dialog-content');
$this->assertNotNull($contents_no_target, 'non-modal dialog opens ( no target ). ');
$id = $contents_no_target->getAttribute('id');
$partial_match = strpos($id, 'drupal-dialog-ajax-testdialog-contents') === 0;
$this->assertTrue($partial_match, 'The non-modal ID has the expected prefix.');
$no_target_button = $no_target_dialog->findButton('Close');
$this->assertNotNull($no_target_button, 'Link dialog has a close button');
$no_target_button->press();
$this->getSession()->getPage()->findButton('Button 1 (modal)')->press();
$button1_dialog = $this->assertSession()->waitForElementVisible('css', 'div.ui-dialog');
$this->assertNotNull($button1_dialog, 'Button opens a modal dialog.');
$button1_dialog_content = $button1_dialog->find('css', 'div.ui-dialog-content');
$this->assertNotNull($button1_dialog_content, 'Button opens a modal dialog.');
// Test the HTML escaping of & character.
$button1_dialog_title = $button1_dialog->find('css', "span.ui-dialog-title:contains('AJAX Dialog & contents')");
$this->assertNotNull($button1_dialog_title);
$button1_dialog_title_amp = $button1_dialog->find('css', "span.ui-dialog-title:contains('AJAX Dialog &amp; contents')");
$this->assertNull($button1_dialog_title_amp);
// Reset: Close the dialog.
$button1_dialog->findButton('Close')->press();
// Abbreviated test for "normal" dialogs, testing only the difference.
$this->getSession()->getPage()->findButton('Button 2 (non-modal)')->press();
$button2_dialog = $this->assertSession()->waitForElementVisible('css', 'div.ui-dialog-content');
$this->assertNotNull($button2_dialog, 'Non-modal content displays as expected.');
// Use a link to close the pagnel opened by button 2.
$this->getSession()->getPage()->clickLink('Link 4 (close non-modal if open)');
// Form modal.
$this->clickLink('Link 5 (form)');
// Two links have been clicked in succession - This time wait for a change
// in the title as the previous closing dialog may temporarily be open.
$form_dialog_title = $this->assertSession()->waitForElementVisible('css', "span.ui-dialog-title:contains('Ajax Form contents')");
$this->assertNotNull($form_dialog_title, 'Dialog form has the expected title.');
// Locate the newly opened dialog.
$form_dialog = $this->getSession()->getPage()->find('css', 'div.ui-dialog');
$this->assertNotNull($form_dialog, 'Form dialog is visible');
$form_contents = $form_dialog->find('css', "p:contains('Ajax Form contents description.')");
$this->assertNotNull($form_contents, 'For has the expected text.');
$do_it = $form_dialog->findButton('Do it');
$this->assertNotNull($do_it, 'The dialog has a "Do it" button.');
$preview = $form_dialog->findButton('Preview');
$this->assertNotNull($preview, 'The dialog contains a "Preview" button.');
// Reset: close the form.
$form_dialog->findButton('Close')->press();
// Non AJAX version of Link 6.
$this->drupalGet('admin/structure/contact/add');
// Check we get a chunk of the code, we can't test the whole form as form
// build id and token with be different.
$contact_form = $this->xpath("//form[@id='contact-form-add-form']");
$this->assertTrue(!empty($contact_form), 'Non-JS entity form page present.');
// Reset: Return to the dialog links page.
$this->drupalGet('ajax-test/dialog');
$this->clickLink('Link 6 (entity form)');
$dialog_add = $this->assertSession()->waitForElementVisible('css', 'div.ui-dialog');
$this->assertNotNull($dialog_add, 'Form dialog is visible');
$form_add = $dialog_add->find('css', 'form.contact-form-add-form');
$this->assertNotNull($form_add, 'Modal dialog JSON contains entity form.');
$form_title = $dialog_add->find('css', "span.ui-dialog-title:contains('Add contact form')");
$this->assertNotNull($form_title, 'The add form title is as expected.');
}
}

View file

@ -0,0 +1,139 @@
<?php
namespace Drupal\FunctionalJavascriptTests\Ajax;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Tests that AJAX-enabled forms work when multiple instances of the same form
* are on a page.
*
* @group Ajax
*/
class MultiFormTest extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'form_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Page']);
// Create a multi-valued field for 'page' nodes to use for Ajax testing.
$field_name = 'field_ajax_test';
FieldStorageConfig::create([
'entity_type' => 'node',
'field_name' => $field_name,
'type' => 'text',
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
])->save();
FieldConfig::create([
'field_name' => $field_name,
'entity_type' => 'node',
'bundle' => 'page',
])->save();
entity_get_form_display('node', 'page', 'default')
->setComponent($field_name, ['type' => 'text_textfield'])
->save();
// Log in a user who can create 'page' nodes.
$this->drupalLogin($this->drupalCreateUser(['create page content']));
}
/**
* Tests that pages with the 'node_page_form' included twice work correctly.
*/
public function testMultiForm() {
// HTML IDs for elements within the field are potentially modified with
// each Ajax submission, but these variables are stable and help target the
// desired elements.
$field_name = 'field_ajax_test';
$form_xpath = '//form[starts-with(@id, "node-page-form")]';
$field_xpath = '//div[contains(@class, "field--name-field-ajax-test")]';
$button_name = $field_name . '_add_more';
$button_value = t('Add another item');
$button_xpath_suffix = '//input[@name="' . $button_name . '"]';
$field_items_xpath_suffix = '//input[@type="text"]';
// Ensure the initial page contains both node forms and the correct number
// of field items and "add more" button for the multi-valued field within
// each form.
$this->drupalGet('form-test/two-instances-of-same-form');
// Wait for javascript on the page to prepare the form attributes.
$this->assertSession()->assertWaitOnAjaxRequest();
$session = $this->getSession();
$page = $session->getPage();
$fields = $page->findAll('xpath', $form_xpath . $field_xpath);
$this->assertEqual(count($fields), 2);
foreach ($fields as $field) {
$this->assertCount(1, $field->findAll('xpath', '.' . $field_items_xpath_suffix), 'Found the correct number of field items on the initial page.');
$this->assertFieldsByValue($field->find('xpath', '.' . $button_xpath_suffix), NULL, 'Found the "add more" button on the initial page.');
}
$this->assertNoDuplicateIds();
// Submit the "add more" button of each form twice. After each corresponding
// page update, ensure the same as above.
for ($i = 0; $i < 2; $i++) {
$forms = $page->find('xpath', $form_xpath);
foreach ($forms as $offset => $form) {
$button = $form->findButton($button_value);
$this->assertNotNull($button, 'Add Another Item button exists');
$button->press();
// Wait for page update.
$this->assertSession()->assertWaitOnAjaxRequest();
// After AJAX request and response page will update.
$page_updated = $session->getPage();
$field = $page_updated->findAll('xpath', '.' . $field_xpath);
$this->assertEqual(count($field[0]->find('xpath', '.' . $field_items_xpath_suffix)), $i + 2, 'Found the correct number of field items after an AJAX submission.');
$this->assertFieldsByValue($field[0]->find('xpath', '.' . $button_xpath_suffix), NULL, 'Found the "add more" button after an AJAX submission.');
$this->assertNoDuplicateIds();
}
}
}
/**
* Asserts that each HTML ID is used for just a single element on the page.
*
* @param string $message
* (optional) A message to display with the assertion.
*/
protected function assertNoDuplicateIds($message = '') {
$args = ['@url' => $this->getUrl()];
if (!$elements = $this->xpath('//*[@id]')) {
$this->fail(new FormattableMarkup('The page @url contains no HTML IDs.', $args));
return;
}
$message = $message ?: new FormattableMarkup('The page @url does not contain duplicate HTML IDs', $args);
$seen_ids = [];
foreach ($elements as $element) {
$id = $element->getAttribute('id');
if (isset($seen_ids[$id])) {
$this->fail($message);
return;
}
$seen_ids[$id] = TRUE;
}
$this->assertTrue(TRUE, $message);
}
}

View file

@ -0,0 +1,106 @@
<?php
namespace Drupal\FunctionalJavascriptTests\Ajax;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Tests the throbber.
*
* @group Ajax
*/
class ThrobberTest extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'node',
'views',
'views_ui',
'views_ui_test_field',
'hold_test',
];
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$admin_user = $this->drupalCreateUser([
'administer views',
]);
$this->drupalLogin($admin_user);
}
/**
* Tests theming throbber element.
*/
public function testThemingThrobberElement() {
$session = $this->getSession();
$web_assert = $this->assertSession();
$page = $session->getPage();
$custom_ajax_progress_indicator_fullscreen = <<<JS
Drupal.theme.ajaxProgressIndicatorFullscreen = function () {
return '<div class="custom-ajax-progress-fullscreen"></div>';
};
JS;
$custom_ajax_progress_throbber = <<<JS
Drupal.theme.ajaxProgressThrobber = function (message) {
return '<div class="custom-ajax-progress-throbber"></div>';
};
JS;
$custom_ajax_progress_message = <<<JS
Drupal.theme.ajaxProgressMessage = function (message) {
return '<div class="custom-ajax-progress-message">Hold door!</div>';
};
JS;
$this->drupalGet('admin/structure/views/view/content');
$this->waitForNoElement('.ajax-progress-fullscreen');
// Test theming fullscreen throbber.
$session->executeScript($custom_ajax_progress_indicator_fullscreen);
hold_test_response(TRUE);
$page->clickLink('Content: Published (grouped)');
$this->assertNotNull($web_assert->waitForElement('css', '.custom-ajax-progress-fullscreen'), 'Custom ajaxProgressIndicatorFullscreen.');
hold_test_response(FALSE);
$this->waitForNoElement('.custom-ajax-progress-fullscreen');
// Test theming throbber message.
$web_assert->waitForElementVisible('css', '[data-drupal-selector="edit-options-group-info-add-group"]');
$session->executeScript($custom_ajax_progress_message);
hold_test_response(TRUE);
$page->pressButton('Add another item');
$this->assertNotNull($web_assert->waitForElement('css', '.ajax-progress-throbber .custom-ajax-progress-message'), 'Custom ajaxProgressMessage.');
hold_test_response(FALSE);
$this->waitForNoElement('.ajax-progress-throbber');
// Test theming throbber.
$web_assert->waitForElementVisible('css', '[data-drupal-selector="edit-options-group-info-group-items-3-title"]');
$session->executeScript($custom_ajax_progress_throbber);
hold_test_response(TRUE);
$page->pressButton('Add another item');
$this->assertNotNull($web_assert->waitForElement('css', '.custom-ajax-progress-throbber'), 'Custom ajaxProgressThrobber.');
hold_test_response(FALSE);
$this->waitForNoElement('.custom-ajax-progress-throbber');
}
/**
* Waits for an element to be removed from the page.
*
* @param string $selector
* CSS selector.
* @param int $timeout
* (optional) Timeout in milliseconds, defaults to 10000.
*
* @todo Remove in https://www.drupal.org/node/2892440.
*/
protected function waitForNoElement($selector, $timeout = 10000) {
$condition = "(typeof jQuery !== 'undefined' && jQuery('$selector').length === 0)";
$this->assertJsCondition($condition, $timeout);
}
}