From 5536bf4f22cc2f246252cb0cd5045a8a5bc937f9 Mon Sep 17 00:00:00 2001 From: Mark Nielsen Date: Wed, 28 Jun 2017 11:20:49 -0700 Subject: [PATCH 1/8] MDL-58948 behat: return the result of the pending JS method This increases compatibility with other Mink drivers, in particular, the Chrome Mink Driver. --- lib/behat/behat_base.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/behat/behat_base.php b/lib/behat/behat_base.php index d01213b4fec..b39da2c28d2 100644 --- a/lib/behat/behat_base.php +++ b/lib/behat/behat_base.php @@ -727,7 +727,7 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext { $pending = ''; try { $jscode = ' - return function() { + return (function() { if (typeof M === "undefined") { if (document.readyState === "complete") { return ""; @@ -741,7 +741,7 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext { } else { return "incomplete" } - }();'; + }());'; $pending = $this->getSession()->evaluateScript($jscode); } catch (NoSuchWindow $nsw) { // We catch an exception here, in case we just closed the window we were interacting with. From 3cf0d01a61acabfc5e0d8f6c5836a8b7dbf36423 Mon Sep 17 00:00:00 2001 From: Mark Nielsen Date: Wed, 28 Jun 2017 11:27:37 -0700 Subject: [PATCH 2/8] MDL-58948 behat: Fix timeouts without a condition The second parameter of the wait method should be a string so passing boolean false was wrong. The default on for the script is 'false' which works cross Mink drivers. --- blocks/comments/tests/behat/behat_block_comments.php | 4 ++-- course/tests/behat/behat_course.php | 2 +- lib/tests/behat/behat_general.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/blocks/comments/tests/behat/behat_block_comments.php b/blocks/comments/tests/behat/behat_block_comments.php index 587695beae8..26f74957f97 100644 --- a/blocks/comments/tests/behat/behat_block_comments.php +++ b/blocks/comments/tests/behat/behat_block_comments.php @@ -66,7 +66,7 @@ class behat_block_comments extends behat_base { $this->find_link(get_string('savecomment'))->click(); // Delay after clicking so that additional comments will have unique time stamps. // We delay 1 second which is all we need. - $this->getSession()->wait(1000, false); + $this->getSession()->wait(1000); } else { @@ -104,7 +104,7 @@ class behat_block_comments extends behat_base { ); // Wait for the animation to finish, in theory is just 1 sec, adding 4 just in case. - $this->getSession()->wait(4 * 1000, false); + $this->getSession()->wait(4 * 1000); } } diff --git a/course/tests/behat/behat_course.php b/course/tests/behat/behat_course.php index 8cdeef65492..4b20f931a1b 100644 --- a/course/tests/behat/behat_course.php +++ b/course/tests/behat/behat_course.php @@ -1830,7 +1830,7 @@ class behat_course extends behat_base { $node->click(); // Smooth expansion. - $this->getSession()->wait(1000, false); + $this->getSession()->wait(1000); } /** diff --git a/lib/tests/behat/behat_general.php b/lib/tests/behat/behat_general.php index 0adb30fd263..0e36d43cd02 100644 --- a/lib/tests/behat/behat_general.php +++ b/lib/tests/behat/behat_general.php @@ -143,7 +143,7 @@ class behat_general extends behat_base { // Wait until the URL change is executed. if ($this->running_javascript()) { - $this->getSession()->wait($waittime * 1000, false); + $this->getSession()->wait($waittime * 1000); } else if (!empty($url)) { // We redirect directly as we can not wait for an automatic redirection. @@ -253,7 +253,7 @@ class behat_general extends behat_base { */ public function i_wait_seconds($seconds) { if ($this->running_javascript()) { - $this->getSession()->wait($seconds * 1000, false); + $this->getSession()->wait($seconds * 1000); } else { sleep($seconds); } From f5459099ae833b476b591e1d9bb1d905abedb2de Mon Sep 17 00:00:00 2001 From: Mark Nielsen Date: Wed, 28 Jun 2017 11:36:36 -0700 Subject: [PATCH 3/8] MDL-58948 behat: catch ElementNotFoundException Another valid exception that can be thrown from a Mink driver is the ElementNotFoundException when an element has gone missing. --- lib/behat/form_field/behat_form_field.php | 2 ++ lib/tests/behat/behat_general.php | 3 +++ 2 files changed, 5 insertions(+) diff --git a/lib/behat/form_field/behat_form_field.php b/lib/behat/form_field/behat_form_field.php index f7f5b9e35b9..56d7124f117 100644 --- a/lib/behat/form_field/behat_form_field.php +++ b/lib/behat/form_field/behat_form_field.php @@ -114,6 +114,8 @@ class behat_form_field { // If the JS handler attached to keydown or keypress destroys the element // the later events may trigger errors because form element no longer exist // or is not visible. Ignore such exceptions here. + } catch (\Behat\Mink\Exception\ElementNotFoundException $e) { + // Other Mink drivers can throw this for the same reason as above. } } diff --git a/lib/tests/behat/behat_general.php b/lib/tests/behat/behat_general.php index 0e36d43cd02..3f7206ef87d 100644 --- a/lib/tests/behat/behat_general.php +++ b/lib/tests/behat/behat_general.php @@ -634,6 +634,9 @@ class behat_general extends behat_base { } catch (WebDriver\Exception\NoSuchElement $e) { // Do nothing just return, as element is no more on page. return true; + } catch (ElementNotFoundException $e) { + // Do nothing just return, as element is no more on page. + return true; } } From b5be387556db448262d5ef9e35373b3e703527c2 Mon Sep 17 00:00:00 2001 From: Mark Nielsen Date: Wed, 28 Jun 2017 11:45:10 -0700 Subject: [PATCH 4/8] MDL-58948 behat: conditionally execute MoodleSelenium2Driver There is some specific code being executed that only exists in the MoodleSelenium2Driver. Conditionally execute this code to make it compatible with other Mink drivers. --- group/tests/behat/behat_groups.php | 7 +++++-- lib/behat/behat_base.php | 9 +++++++-- lib/behat/form_field/behat_form_checkbox.php | 11 +++++++---- lib/behat/form_field/behat_form_select.php | 7 +++++-- lib/tests/behat/behat_general.php | 7 ++++++- 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/group/tests/behat/behat_groups.php b/group/tests/behat/behat_groups.php index d17b22599a1..e314e68efae 100644 --- a/group/tests/behat/behat_groups.php +++ b/group/tests/behat/behat_groups.php @@ -62,8 +62,11 @@ class behat_groups extends behat_base { $select->selectOption($fulloption); // This is needed by some drivers to ensure relevant event is triggred and button is enabled. - $script = "Syn.trigger('change', {}, {{ELEMENT}})"; - $this->getSession()->getDriver()->triggerSynScript($select->getXpath(), $script); + $driver = $this->getSession()->getDriver(); + if ($driver instanceof \Moodle\BehatExtension\Driver\MoodleSelenium2Driver) { + $script = "Syn.trigger('change', {}, {{ELEMENT}})"; + $driver->triggerSynScript($select->getXpath(), $script); + } $this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS); // Here we don't need to wait for the AJAX response. diff --git a/lib/behat/behat_base.php b/lib/behat/behat_base.php index b39da2c28d2..da8f69d10ca 100644 --- a/lib/behat/behat_base.php +++ b/lib/behat/behat_base.php @@ -959,7 +959,12 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext { } $this->ensure_node_is_visible($node); // Ensures hidden elements can't be clicked. $xpath = $node->getXpath(); - $script = "Syn.click({{ELEMENT}})"; - $this->getSession()->getDriver()->triggerSynScript($xpath, $script); + $driver = $this->getSession()->getDriver(); + if ($driver instanceof \Moodle\BehatExtension\Driver\MoodleSelenium2Driver) { + $script = "Syn.click({{ELEMENT}})"; + $driver->triggerSynScript($xpath, $script); + } else { + $driver->click($xpath); + } } } diff --git a/lib/behat/form_field/behat_form_checkbox.php b/lib/behat/form_field/behat_form_checkbox.php index dc568b08e1b..5cd624aca21 100644 --- a/lib/behat/form_field/behat_form_checkbox.php +++ b/lib/behat/form_field/behat_form_checkbox.php @@ -108,9 +108,12 @@ class behat_form_checkbox extends behat_form_field { * Trigger on change event. */ protected function trigger_on_change() { - $this->session->getDriver()->triggerSynScript( - $this->field->getXPath(), - "Syn.trigger('change', {}, {{ELEMENT}})" - ); + $driver = $this->session->getDriver(); + if ($driver instanceof \Moodle\BehatExtension\Driver\MoodleSelenium2Driver) { + $driver->triggerSynScript( + $this->field->getXPath(), + "Syn.trigger('change', {}, {{ELEMENT}})" + ); + } } } diff --git a/lib/behat/form_field/behat_form_select.php b/lib/behat/form_field/behat_form_select.php index 0b16552bb72..6e359fd3f64 100644 --- a/lib/behat/form_field/behat_form_select.php +++ b/lib/behat/form_field/behat_form_select.php @@ -78,8 +78,11 @@ class behat_form_select extends behat_form_field { if (!$node = $this->session->getDriver()->find($dialoguexpath)) { $script = "Syn.trigger('change', {}, {{ELEMENT}})"; try { - $this->session->getDriver()->triggerSynScript($this->field->getXpath(), $script); - $this->session->getDriver()->click('//body//div[@class="skiplinks"]'); + $driver = $this->session->getDriver(); + if ($driver instanceof \Moodle\BehatExtension\Driver\MoodleSelenium2Driver) { + $driver->triggerSynScript($this->field->getXpath(), $script); + } + $driver->click('//body//div[@class="skiplinks"]'); } catch (\Exception $e) { return; } diff --git a/lib/tests/behat/behat_general.php b/lib/tests/behat/behat_general.php index 3f7206ef87d..749540d830f 100644 --- a/lib/tests/behat/behat_general.php +++ b/lib/tests/behat/behat_general.php @@ -1613,7 +1613,12 @@ class behat_general extends behat_base { } // Gets the node based on the requested selector type and locator. $node = $this->get_selected_node($selectortype, $element); - $this->getSession()->getDriver()->post_key("\xEE\x80\x84", $node->getXpath()); + $driver = $this->getSession()->getDriver(); + if ($driver instanceof \Moodle\BehatExtension\Driver\MoodleSelenium2Driver) { + $driver->post_key("\xEE\x80\x84", $node->getXpath()); + } else { + $driver->keyDown($node->getXpath(), "\t"); + } } /** From 95ee1f63920b728bfd66fa2aa371d095e177c706 Mon Sep 17 00:00:00 2001 From: Mark Nielsen Date: Wed, 28 Jun 2017 12:04:19 -0700 Subject: [PATCH 5/8] MDL-58948 course: correct event type listener This event handler is listening for click events on a select element, should be a change event. --- .../moodle-course-management-debug.js | 2 +- .../moodle-course-management/moodle-course-management-min.js | 4 ++-- .../moodle-course-management/moodle-course-management.js | 2 +- course/yui/src/management/js/console.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/course/yui/build/moodle-course-management/moodle-course-management-debug.js b/course/yui/build/moodle-course-management/moodle-course-management-debug.js index abda2075b73..0bef68f036c 100644 --- a/course/yui/build/moodle-course-management/moodle-course-management-debug.js +++ b/course/yui/build/moodle-course-management/moodle-course-management-debug.js @@ -279,7 +279,7 @@ Console.prototype = { if (!this.categoriesinit) { this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'a[data-action]', this); this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'input[name="bcat[]"]', this); - this.get('categorylisting').delegate('click', this.handleBulkSortByaction, '#menuselectsortby', this); + this.get('categorylisting').delegate('change', this.handleBulkSortByaction, '#menuselectsortby', this); this.categoriesinit = true; Y.log(count + ' categories being managed', 'info', 'moodle-course-management'); } else { diff --git a/course/yui/build/moodle-course-management/moodle-course-management-min.js b/course/yui/build/moodle-course-management/moodle-course-management-min.js index 1c994ed8651..2e524a0ae20 100644 --- a/course/yui/build/moodle-course-management/moodle-course-management-min.js +++ b/course/yui/build/moodle-course-management/moodle-course-management-min.js @@ -1,4 +1,4 @@ -YUI.add("moodle-course-management",function(e,t){function n(){n.superclass.constructor.apply(this,arguments)}function r(e){n.superclass.constructor.apply(this,[e])}function i(){i.superclass.constructor.apply(this,arguments)}function s(){s.superclass.constructor.apply(this,arguments)}function o(){o.superclass.constructor.apply(this,arguments)}n.NAME="moodle-course-management",n.CSS_PREFIX="management",n.ATTRS={element:{setter:function(t){return typeof t=="string"&&(t=e.one("#"+t)),t}},categorylisting:{value:null},courselisting:{value:null},coursedetails:{value:null},activecategoryid:{value:null},activecourseid:{value:null},categories:{setter:function(t,n){if(e.Lang.isArray(t))return t;var r=this.get(n);return r.push(t),r},value:[]},courses:{validator:function(t){return e.Lang.isArray(t)},value:[]},page:{getter:function(e,t){return e===null&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},totalpages:{getter:function(e,t){return e===null&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},totalcourses:{getter:function(e,t){return e===null&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},ajaxurl:{getter:function(e){return e===null&&(e=M.cfg.wwwroot+"/course/ajax/management.php"),e},value:null},dragdrop:{value:null}},n.prototype={categoriesinit:!1,initializer:function(){this.set("element","coursecat-management");var e=this.get("element"),t=e.one("#category-listing"),n=e.one("#course-listing"),i=null,s=null;t&&(i=t.one('.listitem[data-selected="1"]')),n&&(s=n.one('.listitem[data-selected="1"]')),this.set("categorylisting",t),this.set("courselisting",n),this.set("coursedetails",e.one("#course-detail")),i&&this.set("activecategoryid",i.getData("id")),s&&this.set("activecourseid",s.getData("id")),this.initialiseCategories(t),this.initialiseCourses(),n&&this.set("dragdrop",new r({console:this}))},initialiseCategories:function(e){var t=0;if(!e)return!1;var n=e.one("#menumovecategoriesto");n&&n.setAttribute("disabled",!0);var r=e.one("#menuresortcategoriesby");r&&r.setAttribute("disabled",!0);var i=e.one("#menuresortcoursesby");i&&i.setAttribute("disabled",!0),e.all(".listitem[data-id]").each(function(e){this.set("categories",new s({node:e,console:this})),t++},this),this.categoriesinit||(this.get("categorylisting").delegate("click",this.handleCategoryDelegation,"a[data-action]",this),this.get("categorylisting").delegate("click",this.handleCategoryDelegation,'input[name="bcat[]"]',this),this.get("categorylisting").delegate("click",this.handleBulkSortByaction,"#menuselectsortby",this),this.categoriesinit=!0)},initialiseCourses:function(){var e=this.getCategoryById(this.get("activecategoryid")),t=this.get("courselisting"),n=0;if(!t)return!1;var r=t.one("#menumovecoursesto");r&&r.setAttribute("disabled",!0),t.all(".listitem[data-id]").each(function(t){this.registerCourse(new o({node:t,console:this,category:e})),n++},this),t.delegate("click",this.handleCourseDelegation,"a[data-action]",this),t.delegate("click",this.handleCourseDelegation,'input[name="bc[]"]',this)},registerCourse:function(e){var t=this.get("courses");t.push(e),this.set("courses",t)},handleCourseDelegation:function(e){var t=e.currentTarget,n=t.getData("action"),r=t.ancestor(".listitem").getData("id"),i=this.getCourseById(r);i&&i.handle(n,e)},handleCategoryDelegation:function(e){var t=e.currentTarget,n=t.getData("action"),r=t.ancestor(".listitem").getData("id"),i=this.getCategoryById(r);i&&i.handle(n,e)},isCourseSelected:function(e){var t=!1;if(e&&e.get("checked"))t=!0;else{var n,r,i=this.get("courses"),s=i.length;for(n=0;n .course-listing"),s=r?r.one("ul.ml"):null,o=i?i.one("ul.ml"):null,u=i?i.getData("canmoveoutof"):!1,a=u?n:o;if(!o)return!1;while(a.get("scrollHeight")===0&&!a.compareTo(window.document.body))a= -a.get("parentNode");o.all("> li").each(function(e){this.initCourseListing(e,a)},this),o.setData("dd",new e.DD.Drop({node:o})),u&&s&&s.all("li > div").each(function(e){this.initCategoryListitem(e)},this),e.DD.DDM.on("drag:start",this.dragStart,this),e.DD.DDM.on("drag:end",this.dragEnd,this),e.DD.DDM.on("drag:drag",this.dragDrag,this),e.DD.DDM.on("drop:over",this.dropOver,this),e.DD.DDM.on("drop:enter",this.dropEnter,this),e.DD.DDM.on("drop:exit",this.dropExit,this),e.DD.DDM.on("drop:hit",this.dropHit,this)},initCourseListing:function(t,n){t.setData("dd",(new e.DD.Drag({node:t,target:{padding:"0 0 0 20"}})).addHandle(".drag-handle").plug(e.Plugin.DDProxy,{moveOnEnd:!1,borderStyle:!1}).plug(e.Plugin.DDConstrained,{constrain2node:n}))},initCategoryListitem:function(t){t.setData("dd",new e.DD.Drop({node:t}))},dragStart:function(e){var t=e.target,n=t.get("node"),r=t.get("dragNode");n.addClass("course-being-dragged"),r.addClass("course-being-dragged-proxy").set("innerHTML",n.one("a.coursename").get("innerHTML")),this.previoussibling=n.get("previousSibling")},dragEnd:function(e){var t=e.target,n=t.get("node");n.removeClass("course-being-dragged"),this.get("console").get("element").all("#category-listing li.highlight").removeClass("highlight")},dragDrag:function(e){var t=e.target.lastXY[1];t div a.action-moveup"),o=i.one(" > div a.action-movedown");if(!a||!o)s=i.one(" > div a.action-moveup"),f=u.one(" > div a.action-movedown"),!a&&!o?(l=e.Node.create(' '),f.replace(l),s.replace(f),l.replace(s),l.destroy()):o||s.insert(f,"after");s=i.one(" > div a.action-moveup"),s?s.focus():(o=i.one(" > div a.action-movedown"),o&&o.focus()),this.updated(!0)}else window.location.reload()},movedown:function(t,n,r){var i,s,o,u,a,f,l,c=this.checkAjaxResponse(t,n,r);if(c===!1)return!1;i=this.get("node"),s=i.next(".listitem");if(s){i.insert(s,"before"),f=s.one(" > div a.action-movedown"),o=i.one(" > div a.action-moveup");if(!f||!o)a=s.one(" > div a.action-moveup"),u=i.one(" > div a.action-movedown"),!f&&!o?(l=e.Node.create(' '),a.replace(l),u.replace(a),l.replace(u),l.destroy()):o||u.insert(a,"before");u=i.one(" > div a.action-movedown"),u?u.focus():(o=i.one(" > div a.action-moveup"),o&&o.focus()),this.updated(!0)}else window.location.reload()},show:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i;if(r===!1)return!1;this.markVisible(),i=this.get("node").one("a[data-action=hide]"),i&&i.focus(),this.updated()},markVisible:function(){return this.get("node").setAttribute("data-visible","1"),!0},hide:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i;if(r===!1)return!1;this.markHidden(),i=this.get("node").one("a[data-action=show]"),i&&i.focus(),this.updated()},markHidden:function(){return this.get("node").setAttribute("data-visible","0"),!0},updated:function(e){e&&this.highlight()},highlight:function(){var e=this.get("node");e.siblings(".highlight").removeClass("highlight"),e.addClass("highlight"),this.highlighttimeout&&window.clearTimeout(this.highlighttimeout),this.highlighttimeout=window.setTimeout(function(){e.removeClass("highlight")},2500)}},e.extend(i,e.Base,i.prototype),s.NAME="moodle-course-management-category",s.CSS_PREFIX="management-category",s.ATTRS={categoryid:{getter:function(e,t){return e===null&&(e=this.get("node").getData("id"),this.set(t,e)),e},value:null,writeOnce:!0},selected:{getter:function(e,t){return e===null&&(e=this.get("node").getData(t),e===null&&(e=!1),this.set(t,e)),e},value:null},courses:{validator:function(t){return e.Lang.isArray(t)},value:[]}},s.prototype={initializer:function(){this.set("itemname","category")},getName:function(){return this.get("node").one("a.categoryname").get("innerHTML")},registerCourse:function(e){var t=this.get("courses");t.push(e),this.set("courses",t)},handle:function(e,t){var n={categoryid:this.get("categoryid")},r=this.get("console").get("activecategoryid");r&&r!==n.categoryid&&(n.selectedcategory=r);switch(e){case"moveup":t.preventDefault(),this.get("console").performAjaxAction("movecategoryup",n,this.moveup,this);break;case"movedown":t.preventDefault(),this.get("console").performAjaxAction("movecategorydown",n,this.movedown,this +YUI.add("moodle-course-management",function(e,t){function n(){n.superclass.constructor.apply(this,arguments)}function r(e){n.superclass.constructor.apply(this,[e])}function i(){i.superclass.constructor.apply(this,arguments)}function s(){s.superclass.constructor.apply(this,arguments)}function o(){o.superclass.constructor.apply(this,arguments)}n.NAME="moodle-course-management",n.CSS_PREFIX="management",n.ATTRS={element:{setter:function(t){return typeof t=="string"&&(t=e.one("#"+t)),t}},categorylisting:{value:null},courselisting:{value:null},coursedetails:{value:null},activecategoryid:{value:null},activecourseid:{value:null},categories:{setter:function(t,n){if(e.Lang.isArray(t))return t;var r=this.get(n);return r.push(t),r},value:[]},courses:{validator:function(t){return e.Lang.isArray(t)},value:[]},page:{getter:function(e,t){return e===null&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},totalpages:{getter:function(e,t){return e===null&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},totalcourses:{getter:function(e,t){return e===null&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},ajaxurl:{getter:function(e){return e===null&&(e=M.cfg.wwwroot+"/course/ajax/management.php"),e},value:null},dragdrop:{value:null}},n.prototype={categoriesinit:!1,initializer:function(){this.set("element","coursecat-management");var e=this.get("element"),t=e.one("#category-listing"),n=e.one("#course-listing"),i=null,s=null;t&&(i=t.one('.listitem[data-selected="1"]')),n&&(s=n.one('.listitem[data-selected="1"]')),this.set("categorylisting",t),this.set("courselisting",n),this.set("coursedetails",e.one("#course-detail")),i&&this.set("activecategoryid",i.getData("id")),s&&this.set("activecourseid",s.getData("id")),this.initialiseCategories(t),this.initialiseCourses(),n&&this.set("dragdrop",new r({console:this}))},initialiseCategories:function(e){var t=0;if(!e)return!1;var n=e.one("#menumovecategoriesto");n&&n.setAttribute("disabled",!0);var r=e.one("#menuresortcategoriesby");r&&r.setAttribute("disabled",!0);var i=e.one("#menuresortcoursesby");i&&i.setAttribute("disabled",!0),e.all(".listitem[data-id]").each(function(e){this.set("categories",new s({node:e,console:this})),t++},this),this.categoriesinit||(this.get("categorylisting").delegate("click",this.handleCategoryDelegation,"a[data-action]",this),this.get("categorylisting").delegate("click",this.handleCategoryDelegation,'input[name="bcat[]"]',this),this.get("categorylisting").delegate("change",this.handleBulkSortByaction,"#menuselectsortby",this),this.categoriesinit=!0)},initialiseCourses:function(){var e=this.getCategoryById(this.get("activecategoryid")),t=this.get("courselisting"),n=0;if(!t)return!1;var r=t.one("#menumovecoursesto");r&&r.setAttribute("disabled",!0),t.all(".listitem[data-id]").each(function(t){this.registerCourse(new o({node:t,console:this,category:e})),n++},this),t.delegate("click",this.handleCourseDelegation,"a[data-action]",this),t.delegate("click",this.handleCourseDelegation,'input[name="bc[]"]',this)},registerCourse:function(e){var t=this.get("courses");t.push(e),this.set("courses",t)},handleCourseDelegation:function(e){var t=e.currentTarget,n=t.getData("action"),r=t.ancestor(".listitem").getData("id"),i=this.getCourseById(r);i&&i.handle(n,e)},handleCategoryDelegation:function(e){var t=e.currentTarget,n=t.getData("action"),r=t.ancestor(".listitem").getData("id"),i=this.getCategoryById(r);i&&i.handle(n,e)},isCourseSelected:function(e){var t=!1;if(e&&e.get("checked"))t=!0;else{var n,r,i=this.get("courses"),s=i.length;for(n=0;n .course-listing"),s=r?r.one("ul.ml"):null,o=i?i.one("ul.ml"):null,u=i?i.getData("canmoveoutof"):!1,a=u?n:o;if(!o)return!1;while(a.get("scrollHeight")===0&&!a.compareTo(window.document.body)) +a=a.get("parentNode");o.all("> li").each(function(e){this.initCourseListing(e,a)},this),o.setData("dd",new e.DD.Drop({node:o})),u&&s&&s.all("li > div").each(function(e){this.initCategoryListitem(e)},this),e.DD.DDM.on("drag:start",this.dragStart,this),e.DD.DDM.on("drag:end",this.dragEnd,this),e.DD.DDM.on("drag:drag",this.dragDrag,this),e.DD.DDM.on("drop:over",this.dropOver,this),e.DD.DDM.on("drop:enter",this.dropEnter,this),e.DD.DDM.on("drop:exit",this.dropExit,this),e.DD.DDM.on("drop:hit",this.dropHit,this)},initCourseListing:function(t,n){t.setData("dd",(new e.DD.Drag({node:t,target:{padding:"0 0 0 20"}})).addHandle(".drag-handle").plug(e.Plugin.DDProxy,{moveOnEnd:!1,borderStyle:!1}).plug(e.Plugin.DDConstrained,{constrain2node:n}))},initCategoryListitem:function(t){t.setData("dd",new e.DD.Drop({node:t}))},dragStart:function(e){var t=e.target,n=t.get("node"),r=t.get("dragNode");n.addClass("course-being-dragged"),r.addClass("course-being-dragged-proxy").set("innerHTML",n.one("a.coursename").get("innerHTML")),this.previoussibling=n.get("previousSibling")},dragEnd:function(e){var t=e.target,n=t.get("node");n.removeClass("course-being-dragged"),this.get("console").get("element").all("#category-listing li.highlight").removeClass("highlight")},dragDrag:function(e){var t=e.target.lastXY[1];t div a.action-moveup"),o=i.one(" > div a.action-movedown");if(!a||!o)s=i.one(" > div a.action-moveup"),f=u.one(" > div a.action-movedown"),!a&&!o?(l=e.Node.create(' '),f.replace(l),s.replace(f),l.replace(s),l.destroy()):o||s.insert(f,"after");s=i.one(" > div a.action-moveup"),s?s.focus():(o=i.one(" > div a.action-movedown"),o&&o.focus()),this.updated(!0)}else window.location.reload()},movedown:function(t,n,r){var i,s,o,u,a,f,l,c=this.checkAjaxResponse(t,n,r);if(c===!1)return!1;i=this.get("node"),s=i.next(".listitem");if(s){i.insert(s,"before"),f=s.one(" > div a.action-movedown"),o=i.one(" > div a.action-moveup");if(!f||!o)a=s.one(" > div a.action-moveup"),u=i.one(" > div a.action-movedown"),!f&&!o?(l=e.Node.create(' '),a.replace(l),u.replace(a),l.replace(u),l.destroy()):o||u.insert(a,"before");u=i.one(" > div a.action-movedown"),u?u.focus():(o=i.one(" > div a.action-moveup"),o&&o.focus()),this.updated(!0)}else window.location.reload()},show:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i;if(r===!1)return!1;this.markVisible(),i=this.get("node").one("a[data-action=hide]"),i&&i.focus(),this.updated()},markVisible:function(){return this.get("node").setAttribute("data-visible","1"),!0},hide:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i;if(r===!1)return!1;this.markHidden(),i=this.get("node").one("a[data-action=show]"),i&&i.focus(),this.updated()},markHidden:function(){return this.get("node").setAttribute("data-visible","0"),!0},updated:function(e){e&&this.highlight()},highlight:function(){var e=this.get("node");e.siblings(".highlight").removeClass("highlight"),e.addClass("highlight"),this.highlighttimeout&&window.clearTimeout(this.highlighttimeout),this.highlighttimeout=window.setTimeout(function(){e.removeClass("highlight")},2500)}},e.extend(i,e.Base,i.prototype),s.NAME="moodle-course-management-category",s.CSS_PREFIX="management-category",s.ATTRS={categoryid:{getter:function(e,t){return e===null&&(e=this.get("node").getData("id"),this.set(t,e)),e},value:null,writeOnce:!0},selected:{getter:function(e,t){return e===null&&(e=this.get("node").getData(t),e===null&&(e=!1),this.set(t,e)),e},value:null},courses:{validator:function(t){return e.Lang.isArray(t)},value:[]}},s.prototype={initializer:function(){this.set("itemname","category")},getName:function(){return this.get("node").one("a.categoryname").get("innerHTML")},registerCourse:function(e){var t=this.get("courses");t.push(e),this.set("courses",t)},handle:function(e,t){var n={categoryid:this.get("categoryid")},r=this.get("console").get("activecategoryid");r&&r!==n.categoryid&&(n.selectedcategory=r);switch(e){case"moveup":t.preventDefault(),this.get("console").performAjaxAction("movecategoryup",n,this.moveup,this);break;case"movedown":t.preventDefault(),this.get("console").performAjaxAction("movecategorydown",n,this.movedown,this );break;case"show":t.preventDefault(),this.get("console").performAjaxAction("showcategory",n,this.show,this);break;case"hide":t.preventDefault(),this.get("console").performAjaxAction("hidecategory",n,this.hide,this);break;case"expand":t.preventDefault(),this.get("node").getData("expanded")==="0"&&(this.get("node").setAttribute("data-expanded","1").setData("expanded","true"),this.get("console").performAjaxAction("getsubcategorieshtml",n,this.loadSubcategories,this)),this.expand();break;case"collapse":t.preventDefault(),this.collapse();break;case"select":var i=this.get("console"),s=i.get("categorylisting").one("#menumovecategoriesto");s&&(i.isCategorySelected(t.currentTarget)&&i.get("categories").length>1?s.removeAttribute("disabled"):s.setAttribute("disabled",!0),i.handleBulkSortByaction());break;default:return!1}},expand:function(){var e=this.get("node"),t=e.one("a[data-action=expand]"),n=e.one("ul[role=group]");e.removeClass("collapsed").setAttribute("aria-expanded","true"),t.setAttribute("data-action","collapse").setAttrs({title:M.util.get_string("collapsecategory","moodle",this.getName())}),require(["core/templates","core/notification"],function(e,n){e.renderPix("t/switch_minus","core",M.util.get_string("collapse","moodle")).then(function(e){t.set("innerHTML",e)}).fail(n.exception)}),n&&n.setAttribute("aria-hidden","false"),this.get("console").performAjaxAction("expandcategory",{categoryid:this.get("categoryid")},null,this)},collapse:function(){var e=this.get("node"),t=e.one("a[data-action=collapse]"),n=e.one("ul[role=group]");e.addClass("collapsed").setAttribute("aria-expanded","false"),t.setAttribute("data-action","expand").setAttrs({title:M.util.get_string("expandcategory","moodle",this.getName())}).one("img").setAttrs({src:M.util.image_url("t/switch_plus","moodle"),alt:M.util.get_string("expand","moodle")}),n&&n.setAttribute("aria-hidden","true"),this.get("console").performAjaxAction("collapsecategory",{categoryid:this.get("categoryid")},null,this)},loadSubcategories:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i=this.get("node"),s=this.get("console"),o,u;return r===!1?!1:(i.append(r.html),s.initialiseCategories(i),M.core&&M.core.actionmenu&&M.core.actionmenu.newDOMNode&&M.core.actionmenu.newDOMNode(i),o=i.one("ul[role=group]"),u=i.one("a[data-action=collapse]"),o&&u&&u.setAttribute("aria-controls",o.generateID()),!0)},moveCourseTo:function(t){var n=this;e.use("moodle-core-notification-confirm",function(){var e=new M.core.confirm({title:M.util.get_string("confirm","moodle"),question:M.util.get_string("confirmcoursemove","moodle",{course:t.getName(),category:n.getName()}),yesLabel:M.util.get_string("move","moodle"),noLabel:M.util.get_string("cancel","moodle")});e.on("complete-yes",function(){e.hide(),e.destroy(),this.get("console").performAjaxAction("movecourseintocategory",{categoryid:this.get("categoryid"),courseid:t.get("courseid")},this.completeMoveCourse,this)},n),e.show()})},completeMoveCourse:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i=this.get("console"),s,o,u;return r===!1?!1:(o=i.getCourseById(n.courseid),o?(this.highlight(),o&&(r.paginationtotals&&(u=i.get("courselisting").one(".listing-pagination-totals"),u&&u.set("innerHTML",r.paginationtotals)),r.totalcatcourses!=="undefined"&&(u=this.get("node").one(".course-count span"),u&&u.set("innerHTML",u.get("innerHTML").replace(/^\d+/,r.totalcatcourses))),typeof r.fromcatcoursecount!="undefined"&&(s=i.get("activecategoryid"),s=i.getCategoryById(s),s&&(u=s.get("node").one(".course-count span"),u&&u.set("innerHTML",u.get("innerHTML").replace(/^\d+/,r.fromcatcoursecount)))),o.remove()),!0):!1)},show:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i;if(r===!1)return!1;this.markVisible(),i=this.get("node").one("a[data-action=hide]"),i&&i.focus(),r.categoryvisibility&&this.updateChildVisibility(r.categoryvisibility),r.coursevisibility&&this.updateCourseVisiblity(r.coursevisibility),this.updated()},hide:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i;if(r===!1)return!1;this.markHidden(),i=this.get("node").one("a[data-action=show]"),i&&i.focus(),r.categoryvisibility&&this.updateChildVisibility(r.categoryvisibility),r.coursevisibility&&this.updateCourseVisiblity(r.coursevisibility),this.updated()},updateCourseVisiblity:function(e){var t=this.get("console"),n,r;try{for(n in e)typeof e[n]=="object"&&(r=t.getCourseById(e[n].id),r&&(e[n].visible==="1"?r.markVisible():r.markHidden()))}catch(i){}return this},updateChildVisibility:function(e){var t=this.get("console"),n,r;try{for(n in e)typeof e[n]=="object"&&(r=t.getCategoryById(e[n].id),r&&(e[n].visible==="1"?r.markVisible():r.markHidden()))}catch(i){}return this}},e.extend(s,i,s.prototype),o.NAME="moodle-course-management-course",o.CSS_PREFIX="management-course",o.ATTRS={courseid:{},selected:{getter:function(e,t){return e===null&&(e=this.get("node").getData(t),this.set(t,e)),e},value:null},node:{},console:{writeOnce:"initOnly"},category:{writeOnce:"initOnly"}},o.prototype={initializer:function(){var e=this.get("node"),t=this.get("category");this.set("courseid",e.getData("id")),t&&t.registerCourse&&t.registerCourse(this),this.set("itemname","course")},getName:function(){return this.get("node").one("a.coursename").get("innerHTML")},handle:function(e,t){var n=this.get("console"),r={courseid:this.get("courseid")};switch(e){case"moveup":t.halt(),n.performAjaxAction("movecourseup",r,this.moveup,this);break;case"movedown":t.halt(),n.performAjaxAction("movecoursedown",r,this.movedown,this);break;case"show":t.halt(),n.performAjaxAction("showcourse",r,this.show,this);break;case"hide":t.halt(),n.performAjaxAction("hidecourse",r,this.hide,this);break;case"select":var i=this.get("console"),s=i.get("courselisting").one("#menumovecoursesto");s&&(i.isCourseSelected(t.currentTarget)?s.removeAttribute("disabled"):s.setAttribute("disabled",!0));break;default:return!1}},remove:function(){this.get("console").removeCourseById(this.get("courseid")),this.get("node").remove()},moveAfter:function( e,t){var n=this.get("console"),r={courseid:this.get("courseid"),moveafter:e,previous:t};n.performAjaxAction("movecourseafter",r,this.moveAfterResponse,this)},moveAfterResponse:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i=this.get("node"),s;if(r===!1)return s=i.ancestor("ul").one("li[data-id="+n.previous+"]"),s?s.insertAfter(i,"after"):i.ancestor("ul").one("li").insert(i,"before"),!1;this.highlight()}},e.extend(o,i,o.prototype)},"@VERSION@",{requires:["base","node","io-base","moodle-core-notification-exception","json-parse","dd-constrain","dd-proxy","dd-drop","dd-delegate","node-event-delegate"]}); diff --git a/course/yui/build/moodle-course-management/moodle-course-management.js b/course/yui/build/moodle-course-management/moodle-course-management.js index 0c7116960c4..10220c93f86 100644 --- a/course/yui/build/moodle-course-management/moodle-course-management.js +++ b/course/yui/build/moodle-course-management/moodle-course-management.js @@ -278,7 +278,7 @@ Console.prototype = { if (!this.categoriesinit) { this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'a[data-action]', this); this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'input[name="bcat[]"]', this); - this.get('categorylisting').delegate('click', this.handleBulkSortByaction, '#menuselectsortby', this); + this.get('categorylisting').delegate('change', this.handleBulkSortByaction, '#menuselectsortby', this); this.categoriesinit = true; } else { } diff --git a/course/yui/src/management/js/console.js b/course/yui/src/management/js/console.js index 633fd47f191..4c7c0342670 100644 --- a/course/yui/src/management/js/console.js +++ b/course/yui/src/management/js/console.js @@ -277,7 +277,7 @@ Console.prototype = { if (!this.categoriesinit) { this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'a[data-action]', this); this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'input[name="bcat[]"]', this); - this.get('categorylisting').delegate('click', this.handleBulkSortByaction, '#menuselectsortby', this); + this.get('categorylisting').delegate('change', this.handleBulkSortByaction, '#menuselectsortby', this); this.categoriesinit = true; Y.log(count + ' categories being managed', 'info', 'moodle-course-management'); } else { From 7cde2bb3131c019ffa4edb3c10b617139dc98dd5 Mon Sep 17 00:00:00 2001 From: Mark Nielsen Date: Wed, 28 Jun 2017 12:40:33 -0700 Subject: [PATCH 6/8] MDL-58948 behat: Chrome driver has unique xpaths Chrome driver has unique xpaths for clicking on all the show more links. So clicking on the first xpath X times does not work. --- lib/tests/behat/behat_forms.php | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/tests/behat/behat_forms.php b/lib/tests/behat/behat_forms.php index 229b5c7e62b..f7e09b373bb 100644 --- a/lib/tests/behat/behat_forms.php +++ b/lib/tests/behat/behat_forms.php @@ -148,13 +148,20 @@ class behat_forms extends behat_base { return; } - // Funny thing about this, with findAll() we specify a pattern and each element matching the pattern is added to the array - // with of xpaths with a [0], [1]... sufix, but when we click on an element it does not matches the specified xpath - // anymore (now is a "Show less..." link) so [1] becomes [0], that's why we always click on the first XPath match, - // will be always the next one. - $iterations = count($showmores); - for ($i = 0; $i < $iterations; $i++) { - $showmores[0]->click(); + if ($this->getSession()->getDriver() instanceof \DMore\ChromeDriver\ChromeDriver) { + // Chrome Driver produces unique xpaths for each element. + foreach ($showmores as $showmore) { + $showmore->click(); + } + } else { + // Funny thing about this, with findAll() we specify a pattern and each element matching the pattern + // is added to the array with of xpaths with a [0], [1]... sufix, but when we click on an element it + // does not matches the specified xpath anymore (now is a "Show less..." link) so [1] becomes [0], + // that's why we always click on the first XPath match, will be always the next one. + $iterations = count($showmores); + for ($i = 0; $i < $iterations; $i++) { + $showmores[0]->click(); + } } } catch (ElementNotFoundException $e) { From f46a2dd91378b75fbf716fea62d1dc37afdf2439 Mon Sep 17 00:00:00 2001 From: Mark Nielsen Date: Wed, 28 Jun 2017 12:42:50 -0700 Subject: [PATCH 7/8] MDL-58948 behat: Fix spin method when callback throws exception Was preventing the sleep statements from running. This caused steps to fail too early when it was just waiting for something to change in the browser. --- lib/behat/behat_base.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/behat/behat_base.php b/lib/behat/behat_base.php index da8f69d10ca..bb8a52b0e6c 100644 --- a/lib/behat/behat_base.php +++ b/lib/behat/behat_base.php @@ -335,8 +335,6 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext { if (!$exception) { $exception = $e; } - // We wait until no exception is thrown or timeout expires. - continue; } if ($this->running_javascript()) { From b095b36c02bbbf2806e5dfa493cc272b1ec740e7 Mon Sep 17 00:00:00 2001 From: Mark Nielsen Date: Wed, 28 Jun 2017 12:45:07 -0700 Subject: [PATCH 8/8] MDL-58948 behat: capture DriverException Other drivers may throw a DriverException when the popup or iframe disappears. --- lib/behat/behat_base.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/behat/behat_base.php b/lib/behat/behat_base.php index bb8a52b0e6c..e7c631fada5 100644 --- a/lib/behat/behat_base.php +++ b/lib/behat/behat_base.php @@ -28,7 +28,8 @@ // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. -use Behat\Mink\Exception\ExpectationException as ExpectationException, +use Behat\Mink\Exception\DriverException, + Behat\Mink\Exception\ExpectationException as ExpectationException, Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException, Behat\Mink\Element\NodeElement as NodeElement; @@ -881,6 +882,8 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext { } catch (NoSuchWindow $e) { // If we were interacting with a popup window it will not exists after closing it. + } catch (DriverException $e) { + // Same reason as above. } }