From d073f5daf09ebf5c07c4b441a456d0a4772a8f61 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Thu, 12 Sep 2019 14:19:11 +0800 Subject: [PATCH 1/2] MDL-66550 theme: Wrap JS calls in pendingJS --- lib/outputfragmentrequirementslib.php | 4 +++- lib/outputrequirementslib.php | 18 ++++++++++++++---- theme/boost/amd/build/loader.min.js | 2 +- theme/boost/amd/src/loader.js | 2 ++ theme/boost/templates/columns1.mustache | 5 ++++- theme/boost/templates/columns2.mustache | 7 ++++++- theme/boost/templates/embedded.mustache | 5 ++++- theme/boost/templates/login.mustache | 5 ++++- theme/boost/templates/maintenance.mustache | 5 ++++- theme/boost/templates/secure.mustache | 5 ++++- 10 files changed, 46 insertions(+), 12 deletions(-) diff --git a/lib/outputfragmentrequirementslib.php b/lib/outputfragmentrequirementslib.php index 0ae13fbb789..da753ef3332 100644 --- a/lib/outputfragmentrequirementslib.php +++ b/lib/outputfragmentrequirementslib.php @@ -57,8 +57,10 @@ class fragment_requirements_manager extends page_requirements_manager { $output = ''; // First include must be to a module with no dependencies, this prevents multiple requests. - $prefix = "require(['core/first'], function() {\n"; + $prefix = 'M.util.js_pending("core/first");'; + $prefix .= "require(['core/first'], function() {\n"; $suffix = "\n});"; + $suffix .= 'M.util.js_complete("core/first");'; $output .= html_writer::script($prefix . implode(";\n", $this->amdjscode) . $suffix); return $output; } diff --git a/lib/outputrequirementslib.php b/lib/outputrequirementslib.php index 4e56647ad59..bdd10cf5826 100644 --- a/lib/outputrequirementslib.php +++ b/lib/outputrequirementslib.php @@ -1015,6 +1015,8 @@ class page_requirements_manager { $module = clean_param($module, PARAM_ALPHANUMEXT); $func = clean_param($func, PARAM_ALPHANUMEXT); + $modname = "{$component}/{$module}"; + $jsonparams = array(); foreach ($params as $param) { $jsonparams[] = json_encode($param); @@ -1027,9 +1029,15 @@ class page_requirements_manager { '"). Generally there are better ways to pass lots of data from PHP to JavaScript, for example via Ajax, data attributes, ... . ' . 'This warning is triggered if the argument string becomes longer than ' . $toomanyparamslimit . ' characters.', DEBUG_DEVELOPER); } - } - $js = 'require(["' . $component . '/' . $module . '"], function(amd) { amd.' . $func . '(' . $strparams . '); });'; + } + $js = <<js_amd_inline($js); } @@ -1343,8 +1351,10 @@ class page_requirements_manager { } // First include must be to a module with no dependencies, this prevents multiple requests. - $prefix = "require(['core/first'], function() {\n"; - $suffix = "\n});"; + $prefix = 'M.util.js_pending("core/first");'; + $prefix .= "require(['core/first'], function() {\n"; + $suffix = 'M.util.js_complete("core/first");'; + $suffix .= "\n});"; $output .= html_writer::script($prefix . implode(";\n", $this->amdjscode) . $suffix); return $output; } diff --git a/theme/boost/amd/build/loader.min.js b/theme/boost/amd/build/loader.min.js index 6dcbf5e67c0..06b9b4baec9 100644 --- a/theme/boost/amd/build/loader.min.js +++ b/theme/boost/amd/build/loader.min.js @@ -1 +1 @@ -define(["jquery","./tether","core/event"],function(a,b,c){return window.jQuery=a,window.Tether=b,require(["theme_boost/aria","theme_boost/util","theme_boost/alert","theme_boost/button","theme_boost/carousel","theme_boost/collapse","theme_boost/dropdown","theme_boost/modal","theme_boost/scrollspy","theme_boost/tab","theme_boost/tooltip","theme_boost/popover"],function(b){a("body").popover({trigger:"focus",selector:"[data-toggle=popover][data-trigger!=hover]"}),a("html").popover({container:"body",selector:"[data-toggle=popover][data-trigger=hover]",trigger:"hover",delay:{hide:500}}),c.getLegacyEvents().done(function(b){a(document).on(b.FILTER_CONTENT_UPDATED,function(){a("body").popover({selector:'[data-toggle="popover"]',trigger:"focus"})})}),b.init()}),{}}); \ No newline at end of file +define(["jquery","./tether","core/event"],function(a,b,c){return window.jQuery=a,window.Tether=b,M.util.js_pending("theme_boost/loader:children"),require(["theme_boost/aria","theme_boost/util","theme_boost/alert","theme_boost/button","theme_boost/carousel","theme_boost/collapse","theme_boost/dropdown","theme_boost/modal","theme_boost/scrollspy","theme_boost/tab","theme_boost/tooltip","theme_boost/popover"],function(b){a("body").popover({trigger:"focus",selector:"[data-toggle=popover][data-trigger!=hover]"}),a("html").popover({container:"body",selector:"[data-toggle=popover][data-trigger=hover]",trigger:"hover",delay:{hide:500}}),c.getLegacyEvents().done(function(b){a(document).on(b.FILTER_CONTENT_UPDATED,function(){a("body").popover({selector:'[data-toggle="popover"]',trigger:"focus"})})}),b.init(),M.util.js_complete("theme_boost/loader:children")}),{}}); \ No newline at end of file diff --git a/theme/boost/amd/src/loader.js b/theme/boost/amd/src/loader.js index 8ab1706af09..bec7062644f 100644 --- a/theme/boost/amd/src/loader.js +++ b/theme/boost/amd/src/loader.js @@ -27,6 +27,7 @@ define(['jquery', './tether', 'core/event'], function(jQuery, Tether, Event) { window.jQuery = jQuery; window.Tether = Tether; + M.util.js_pending('theme_boost/loader:children'); require(['theme_boost/aria', 'theme_boost/util', @@ -69,6 +70,7 @@ define(['jquery', './tether', 'core/event'], function(jQuery, Tether, Event) { }); Aria.init(); + M.util.js_complete('theme_boost/loader:children'); }); diff --git a/theme/boost/templates/columns1.mustache b/theme/boost/templates/columns1.mustache index d31db85d62b..8870707c16a 100644 --- a/theme/boost/templates/columns1.mustache +++ b/theme/boost/templates/columns1.mustache @@ -65,5 +65,8 @@ {{#js}} -require(['theme_boost/loader']); +M.util.js_pending('theme_boost/loader'); +require(['theme_boost/loader'], function() { + M.util.js_complete('theme_boost/loader'); +}); {{/js}} diff --git a/theme/boost/templates/columns2.mustache b/theme/boost/templates/columns2.mustache index 51b8d4d25f6..1006cea1b79 100644 --- a/theme/boost/templates/columns2.mustache +++ b/theme/boost/templates/columns2.mustache @@ -97,8 +97,13 @@ {{#js}} -require(['theme_boost/loader']); +M.util.js_pending('theme_boost/loader'); +require(['theme_boost/loader'], function() { + M.util.js_complete('theme_boost/loader'); +}); +M.util.js_pending('theme_boost/drawer'); require(['theme_boost/drawer'], function(mod) { mod.init(); + M.util.js_complete('theme_boost/drawer'); }); {{/js}} diff --git a/theme/boost/templates/embedded.mustache b/theme/boost/templates/embedded.mustache index e38dab69ffc..ce62a560154 100644 --- a/theme/boost/templates/embedded.mustache +++ b/theme/boost/templates/embedded.mustache @@ -53,5 +53,8 @@ {{#js}} -require(['theme_boost/loader']); +M.util.js_pending('theme_boost/loader'); +require(['theme_boost/loader'], function() { + M.util.js_complete('theme_boost/loader'); +}); {{/js}} diff --git a/theme/boost/templates/login.mustache b/theme/boost/templates/login.mustache index adae2c86f5f..71859c02877 100644 --- a/theme/boost/templates/login.mustache +++ b/theme/boost/templates/login.mustache @@ -67,5 +67,8 @@ {{#js}} -require(['theme_boost/loader']); +M.util.js_pending('theme_boost/loader'); +require(['theme_boost/loader'], function() { + M.util.js_complete('theme_boost/loader'); +}); {{/js}} diff --git a/theme/boost/templates/maintenance.mustache b/theme/boost/templates/maintenance.mustache index c53d65d55de..e89de50293a 100644 --- a/theme/boost/templates/maintenance.mustache +++ b/theme/boost/templates/maintenance.mustache @@ -74,5 +74,8 @@ {{#js}} -require(['theme_boost/loader']); +M.util.js_pending('theme_boost/loader'); +require(['theme_boost/loader'], function() { + M.util.js_complete('theme_boost/loader'); +}); {{/js}} diff --git a/theme/boost/templates/secure.mustache b/theme/boost/templates/secure.mustache index 5a1a5dbf1f7..c257371aad4 100644 --- a/theme/boost/templates/secure.mustache +++ b/theme/boost/templates/secure.mustache @@ -86,5 +86,8 @@ {{#js}} -require(['theme_boost/loader']); +M.util.js_pending('theme_boost/loader'); +require(['theme_boost/loader'], function() { + M.util.js_complete('theme_boost/loader'); +}); {{/js}} From a5cdde3c796bae991c5c1b188aec94ad944f15ac Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Thu, 12 Sep 2019 14:18:36 +0800 Subject: [PATCH 2/2] MDL-66550 admin: The execute wrapper MUST be used in behat --- admin/tests/behat/behat_admin.php | 34 +++---------------- .../behat/behat_theme_boost_behat_admin.php | 31 +++-------------- 2 files changed, 9 insertions(+), 56 deletions(-) diff --git a/admin/tests/behat/behat_admin.php b/admin/tests/behat/behat_admin.php index d8ec74c9c5a..04536bc6015 100644 --- a/admin/tests/behat/behat_admin.php +++ b/admin/tests/behat/behat_admin.php @@ -48,7 +48,6 @@ class behat_admin extends behat_base { * @param TableNode $table */ public function i_set_the_following_administration_settings_values(TableNode $table) { - if (!$data = $table->getRowsHash()) { return; } @@ -57,17 +56,12 @@ class behat_admin extends behat_base { // We expect admin block to be visible, otherwise go to homepage. if (!$this->getSession()->getPage()->find('css', '.block_settings')) { - $this->getSession()->visit($this->locate_path('/')); - $this->wait(self::get_timeout() * 1000, self::PAGE_READY_JS); + $this->execute('behat_forms::i_am_on_homepage'); } // Search by label. - $searchbox = $this->find_field(get_string('searchinsettings', 'admin')); - $searchbox->setValue($label); - $submitsearch = $this->find('css', 'form.adminsearchform input[type=submit]'); - $submitsearch->press(); - - $this->wait(self::get_timeout() * 1000, self::PAGE_READY_JS); + $this->execute('behat_forms::i_set_the_field_to', [get_string('searchinsettings', 'admin'), $label]); + $this->execute("behat_general::i_click_on_in_the", [get_string('search', 'admin'), 'button', '.block_settings', 'css_element']); // Admin settings does not use the same DOM structure than other moodle forms // but we also need to use lib/behat/form_field/* to deal with the different moodle form elements. @@ -83,35 +77,17 @@ class behat_admin extends behat_base { "@id=//span[contains(normalize-space(.), $label)]/preceding-sibling::label[1]/@for]"; $fieldnode = $this->find('xpath', $fieldxpath, $exception); - $formfieldtypenode = $this->find('xpath', $fieldxpath . "/ancestor::div[@class='form-setting']" . - "/child::div[contains(concat(' ', @class, ' '), ' form-')]/child::*/parent::div"); - } catch (ElementNotFoundException $e) { - // Multi element settings, interacting only the first one. $fieldxpath = "//*[label[normalize-space(.)= $label]|span[normalize-space(.)= $label]]/" . "ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' form-item ')]" . "/descendant::div[@class='form-group']/descendant::*[self::input | self::textarea | self::select]" . "[not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]"; $fieldnode = $this->find('xpath', $fieldxpath); - - // It is the same one that contains the type. - $formfieldtypenode = $fieldnode; } - // Getting the class which contains the field type. - $classes = explode(' ', $formfieldtypenode->getAttribute('class')); - foreach ($classes as $class) { - if (substr($class, 0, 5) == 'form-') { - $type = substr($class, 5); - } - } - - // Instantiating the appropiate field type. - $field = behat_field_manager::get_field_instance($type, $fieldnode, $this->getSession()); - $field->set_value($value); - - $this->find_button(get_string('savechanges'))->press(); + $this->execute('behat_forms::i_set_the_field_with_xpath_to', [$fieldxpath, $value]); + $this->execute("behat_general::i_click_on", [get_string('savechanges'), 'button']); } } diff --git a/theme/boost/tests/behat/behat_theme_boost_behat_admin.php b/theme/boost/tests/behat/behat_theme_boost_behat_admin.php index b75e4dbbd7c..dd0f3b40ab6 100644 --- a/theme/boost/tests/behat/behat_theme_boost_behat_admin.php +++ b/theme/boost/tests/behat/behat_theme_boost_behat_admin.php @@ -48,12 +48,8 @@ class behat_theme_boost_behat_admin extends behat_admin { $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', [get_string('administrationsite')]); // Search by label. - $searchbox = $this->find_field(get_string('query', 'admin')); - $searchbox->setValue($label); - $submitsearch = $this->find('css', 'form input[type=submit][name=search]'); - $submitsearch->press(); - - $this->wait(self::get_timeout() * 1000, self::PAGE_READY_JS); + $this->execute('behat_forms::i_set_the_field_to', [get_string('query', 'admin'), $label]); + $this->execute("behat_general::i_click_on", [get_string('search', 'admin'), 'button']); // Admin settings does not use the same DOM structure than other moodle forms // but we also need to use lib/behat/form_field/* to deal with the different moodle form elements. @@ -69,11 +65,6 @@ class behat_theme_boost_behat_admin extends behat_admin { "[@id=//label[contains(normalize-space(.), $label)]/@for or " . "@id=//span[contains(normalize-space(.), $label)]/preceding-sibling::label[1]/@for]"; $fieldnode = $this->find('xpath', $fieldxpath, $exception); - - $formfieldtypenode = $this->find('xpath', $fieldxpath . - "/ancestor::div[contains(concat(' ', @class, ' '), ' form-setting ')]" . - "/child::div[contains(concat(' ', @class, ' '), ' form-')]/child::*/parent::div"); - } catch (ElementNotFoundException $e) { // Multi element settings, interacting only the first one. @@ -83,25 +74,11 @@ class behat_theme_boost_behat_admin extends behat_admin { "/descendant::*[self::input | self::textarea | self::select]" . "[not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]"; $fieldnode = $this->find('xpath', $fieldxpath); - - // It is the same one that contains the type. - $formfieldtypenode = $fieldnode; } - // Getting the class which contains the field type. - $classes = explode(' ', $formfieldtypenode->getAttribute('class')); - $type = false; - foreach ($classes as $class) { - if (substr($class, 0, 5) == 'form-') { - $type = substr($class, 5); - } - } + $this->execute('behat_forms::i_set_the_field_with_xpath_to', [$fieldxpath, $value]); - // Instantiating the appropiate field type. - $field = behat_field_manager::get_field_instance($type, $fieldnode, $this->getSession()); - $field->set_value($value); - - $this->find_button(get_string('savechanges'))->press(); + $this->execute("behat_general::i_click_on", [get_string('savechanges'), 'button']); } }