From f07d3b71d288d688152d8e1ca2e3f2a6778c329b Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 15 Jun 2020 09:44:39 +0800 Subject: [PATCH] MDL-69107 form_autocomplete: Rewrite item selection The form_autocomplete is essentially a custom element. Unfortunately the `setValue()` function in Mink has undesired actions so it is necessary to write our own handling for it. The standard Mink `setValue()` function focuses the element, sets a value, and then blurs the element. In the case of the autocomplete this can cause the autocomplete suggestions list to be closed in some situations. Instead of using the setValue we click, and type the value, but do not immediately blur. --- lib/amd/build/form-autocomplete.min.js | Bin 12637 -> 12691 bytes lib/amd/build/form-autocomplete.min.js.map | Bin 83790 -> 84010 bytes lib/amd/src/form-autocomplete.js | 3 + lib/behat/behat_field_manager.php | 5 +- .../form_field/behat_form_autocomplete.php | 86 ++++++++++++------ .../form_autocomplete_input.mustache | 26 +++++- lib/tests/behat/behat_forms.php | 2 - 7 files changed, 90 insertions(+), 32 deletions(-) diff --git a/lib/amd/build/form-autocomplete.min.js b/lib/amd/build/form-autocomplete.min.js index 23e42cba9f7442d3754e6b6c498f0b3b6c13cd1c..71d7b14e7da298172e83967fc2b8dac59b224065 100644 GIT binary patch delta 65 zcmcbcG&y;LjlM~;URq{eiiT2VUO{PzlBQl_NlB50Qc7Y;qHbDdYEDW?WkITvj#6T2 UNq%yEZb43JNvhIjD}7}q02ZhhApigX delta 12 TcmbQ7d^c%>jsE5$eMKe!C7T4u diff --git a/lib/amd/build/form-autocomplete.min.js.map b/lib/amd/build/form-autocomplete.min.js.map index 5c1fe60e9260df5d1593aaae9c3e596f2a72f468..89cf819acc6975d1e41893e00a6b9bcd01157062 100644 GIT binary patch delta 320 zcmY+8u}i~17{#fOKcR}?aNAXSHa0=1<9BBedp3fDx;UK8#YEF2&0SjBKSG3{yQ`DX zK|#db!Og$M#Y;Aq$M<{hd+@&Rtmk{{g!hjJ5z-wXokQ@Q{^?goG04|6JkkQlH z&1r2fD7mHp%LW`q#sDreUmoo8)u+O1?LHkDcBnsPrsf)A;8efmsJ-7LUFs`p61z#d z)#Yrse#7d{E%WD;a7YX>L)FKu*eV)O?95nuycs*-`oK^$UGe$kt8d%LOI2A&q$8n? zNE8SycEVUDfnMYi#R@5@QJ59zN=Bl}e>UA-7^gu8$7x<_yxkL87ajb+hY7->)Y&+j R>kkLYW<3A^ delta 144 zcmZ2Af%RM;>xSLMEUu0wE}Pj))R{!foP2d$9m}2k3Y~R49V>u5XUFo%&Sk!0#$Xu} zC%8nduY{+VC0I(^$*getAttribute('type'); switch ($type) { case 'text': + if ($fieldtype = $fieldnode->getAttribute('data-fieldtype')) { + return self::normalise_fieldtype($fieldtype); + } + return 'text'; case 'password': case 'email': case 'file': diff --git a/lib/behat/form_field/behat_form_autocomplete.php b/lib/behat/form_field/behat_form_autocomplete.php index 238bbeecc35..6c4b59c6bef 100644 --- a/lib/behat/form_field/behat_form_autocomplete.php +++ b/lib/behat/form_field/behat_form_autocomplete.php @@ -48,43 +48,75 @@ class behat_form_autocomplete extends behat_form_text { throw new coding_exception('Setting the value of an autocomplete field requires javascript.'); } - // Set the value of the autocomplete's input. - // If this autocomplete offers suggestions then these should be fetched by setting the value and waiting for the - // JS to finish fetching those suggestions. + // Clear all current selections. + $rootnode = $this->field->getParent()->getParent(); + $selections = $rootnode->findAll('css', '.form-autocomplete-selection [role=option]'); + foreach ($selections as $selection) { + $selection->click(); + $this->wait_for_pending_js(); + } - $istagelement = $this->field->hasAttribute('data-tags') && $this->field->getAttribute('data-tags'); + $allowscreation = $this->field->hasAttribute('data-tags') && !empty($this->field->getAttribute('data-tags')); + $hasmultiple = $this->field->hasAttribute('data-multiple') && !empty($this->field->getAttribute('data-multiple')); - if ($istagelement && false !== strpos($value, ',')) { - // Commas have a special meaning as a value separator in 'tag' autocomplete elements. + if ($hasmultiple && false !== strpos($value, ',')) { + // Commas have a special meaning as a value separator in 'multiple' autocomplete elements. // To handle this we break the value up by comma, and enter it in chunks. $values = explode(',', $value); while ($value = array_shift($values)) { - $this->set_value($value); + $this->add_value(trim($value), $allowscreation); } } else { - $this->field->setValue($value); - $this->wait_for_pending_js(); + $this->add_value(trim($value), $allowscreation); + } + } - // If the autocomplete found suggestions, then it will have: - // 1) marked itself as expanded; and - // 2) have an aria-selected suggestion in the list. - $expanded = $this->field->getAttribute('aria-expanded'); - $suggestion = $this->field->getParent()->find('css', '.form-autocomplete-suggestions > [aria-selected="true"]'); - - if ($expanded && null !== $suggestion) { - // A suggestion was found. - // Click on the first item in the list. - $suggestion->click(); - } else { - // Press the return key to create a new tag. - behat_base::type_keys($this->session, [behat_keys::ENTER]); - } - $this->wait_for_pending_js(); + /** + * Add a value to the autocomplete. + * + * @param string $value + * @param bool $allowscreation + */ + protected function add_value(string $value, bool $allowscreation): void { + $value = trim($value); - // Press the escape to close the autocomplete suggestions list. - behat_base::type_keys($this->session, [behat_keys::ESCAPE]); - $this->wait_for_pending_js(); + // Click into the field. + $this->field->click(); + + // Remove any existing text. + do { + behat_base::type_keys($this->session, [behat_keys::BACKSPACE, behat_keys::DELETE]); + } while (strlen($this->field->getValue()) > 0); + $this->wait_for_pending_js(); + + // Type in the new value. + behat_base::type_keys($this->session, str_split($value)); + $this->wait_for_pending_js(); + + // If the autocomplete found suggestions, then it will have: + // 1) marked itself as expanded; and + // 2) have an aria-selected suggestion in the list. + $expanded = $this->field->getAttribute('aria-expanded'); + $suggestion = $this->field->getParent()->getParent()->find('css', '.form-autocomplete-suggestions > [aria-selected="true"]'); + + if ($expanded && null !== $suggestion) { + // A suggestion was found. + // Click on the first item in the list. + $suggestion->click(); + } else if ($allowscreation) { + // Press the return key to create a new entry. + behat_base::type_keys($this->session, [behat_keys::ENTER]); + } else { + throw new \InvalidArgumentException( + "Unable to find '{$value}' in the list of options, and unable to create a new option" + ); } + + $this->wait_for_pending_js(); + + // Press the escape to close the autocomplete suggestions list. + behat_base::type_keys($this->session, [behat_keys::ESCAPE]); + $this->wait_for_pending_js(); } } diff --git a/lib/templates/form_autocomplete_input.mustache b/lib/templates/form_autocomplete_input.mustache index ac76ed575ea..ae7d1943d8a 100644 --- a/lib/templates/form_autocomplete_input.mustache +++ b/lib/templates/form_autocomplete_input.mustache @@ -37,13 +37,35 @@ }} {{#showSuggestions}}
- +
{{/showSuggestions}} {{^showSuggestions}}
- +
{{/showSuggestions}} diff --git a/lib/tests/behat/behat_forms.php b/lib/tests/behat/behat_forms.php index 2532f943175..1d2a3e47b70 100644 --- a/lib/tests/behat/behat_forms.php +++ b/lib/tests/behat/behat_forms.php @@ -693,8 +693,6 @@ class behat_forms extends behat_base { $xpathtarget = "//ul[@class='form-autocomplete-suggestions']//*[contains(concat('|', string(.), '|'),'|" . $item . "|')]"; $this->execute('behat_general::i_click_on', [$xpathtarget, 'xpath_element']); - - $this->execute('behat_general::i_press_key_in_element', ['13', 'body', 'xpath_element']); } /** -- 2.43.0