mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 05:58:34 +01:00
Merge branch 'MDL-42625_master' of git://github.com/dmonllao/moodle
This commit is contained in:
commit
ebc77165a4
@ -59,7 +59,7 @@ 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::TIMEOUT, '(document.readyState === "complete")');
|
||||
$this->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
}
|
||||
|
||||
// Search by label.
|
||||
@ -68,7 +68,7 @@ class behat_admin extends behat_base {
|
||||
$submitsearch = $this->find('css', 'form.adminsearchform input[type=submit]');
|
||||
$submitsearch->press();
|
||||
|
||||
$this->wait(self::TIMEOUT, '(document.readyState === "complete")');
|
||||
$this->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
|
||||
// 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.
|
||||
|
@ -42,5 +42,4 @@ Feature: Upload users
|
||||
And I expand "Users" node
|
||||
And I follow "Groups"
|
||||
And I select "Section 1 (1)" from "groups"
|
||||
And I wait "4" seconds
|
||||
And the "members" select box should contain "Tom Jones"
|
||||
|
@ -39,7 +39,7 @@ Feature: Page contents assertions
|
||||
| Course 1 | C1 | 0 |
|
||||
And I log in as "admin"
|
||||
And I follow "Course 1"
|
||||
When I click on "Move this to the dock" "button" in the "Administration" "block"
|
||||
When I dock "Administration" block
|
||||
Then I should not see "Question bank" in the "region-pre" "region"
|
||||
And I click on "//div[@id='dock']/descendant::h2[normalize-space(.)='Administration']" "xpath_element"
|
||||
|
||||
@ -49,5 +49,5 @@ Feature: Page contents assertions
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And I log in as "admin"
|
||||
When I click on "Move this to the dock" "button" in the "Administration" "block"
|
||||
When I dock "Administration" block
|
||||
Then I should not see "Turn editing on" in the "region-pre" "region"
|
||||
|
@ -230,8 +230,6 @@ Feature: Set up contextual data for tests
|
||||
Then the "groups" select box should contain "Group 1 (1)"
|
||||
And the "groups" select box should contain "Group 2 (1)"
|
||||
And I select "Group 1 (1)" from "groups"
|
||||
And I wait "5" seconds
|
||||
And the "members" select box should contain "Student 1"
|
||||
And I select "Group 2 (1)" from "groups"
|
||||
And I wait "5" seconds
|
||||
And the "members" select box should contain "Student 2"
|
||||
|
@ -38,7 +38,7 @@ Feature: Backup Moodle courses
|
||||
And I press "Continue"
|
||||
And I click on "Continue" "button" in the ".bcs-current-course" "css_element"
|
||||
And "//div[contains(concat(' ', normalize-space(@class), ' '), ' fitem ')][contains(., 'Include calendar events')]/descendant::img" "xpath_element" should exists
|
||||
And I check "Include course logs"
|
||||
And "Include course logs" "checkbox" should exists
|
||||
And I press "Next"
|
||||
|
||||
@javascript
|
||||
|
@ -56,27 +56,32 @@ class behat_backup extends behat_base {
|
||||
|
||||
// Go to homepage.
|
||||
$this->getSession()->visit($this->locate_path('/'));
|
||||
$this->wait();
|
||||
|
||||
// Click the course link.
|
||||
$this->find_link($backupcourse)->click();
|
||||
$this->wait();
|
||||
|
||||
// Click the backup link.
|
||||
$this->find_link(get_string('backup'))->click();
|
||||
$this->wait();
|
||||
|
||||
// Initial settings.
|
||||
$this->fill_backup_restore_form($options);
|
||||
$this->find_button(get_string('backupstage1action', 'backup'))->press();
|
||||
$this->wait();
|
||||
|
||||
// Schema settings.
|
||||
$this->fill_backup_restore_form($options);
|
||||
$this->find_button(get_string('backupstage2action', 'backup'))->press();
|
||||
$this->wait();
|
||||
|
||||
// Confirmation and review, backup filename can also be specified.
|
||||
$this->fill_backup_restore_form($options);
|
||||
$this->find_button(get_string('backupstage4action', 'backup'))->press();
|
||||
|
||||
// Waiting for it to finish.
|
||||
$this->wait(10);
|
||||
$this->wait(self::EXTENDED_TIMEOUT);
|
||||
|
||||
// Last backup continue button.
|
||||
$this->find_button(get_string('backupstage16action', 'backup'))->press();
|
||||
@ -101,12 +106,15 @@ class behat_backup extends behat_base {
|
||||
|
||||
// Go to homepage.
|
||||
$this->getSession()->visit($this->locate_path('/'));
|
||||
$this->wait();
|
||||
|
||||
// Click the course link.
|
||||
$this->find_link($tocourse)->click();
|
||||
$this->wait();
|
||||
|
||||
// Click the import link.
|
||||
$this->find_link(get_string('import'))->click();
|
||||
$this->wait();
|
||||
|
||||
// Select the course.
|
||||
$exception = new ExpectationException('"' . $fromcourse . '" course not found in the list of courses to import from', $this->getSession());
|
||||
@ -121,18 +129,21 @@ class behat_backup extends behat_base {
|
||||
$radionode->click();
|
||||
|
||||
$this->find_button(get_string('continue'))->press();
|
||||
$this->wait();
|
||||
|
||||
// Initial settings.
|
||||
$this->fill_backup_restore_form($options);
|
||||
$this->find_button(get_string('importbackupstage1action', 'backup'))->press();
|
||||
$this->wait();
|
||||
|
||||
// Schema settings.
|
||||
$this->fill_backup_restore_form($options);
|
||||
$this->find_button(get_string('importbackupstage2action', 'backup'))->press();
|
||||
$this->wait();
|
||||
|
||||
// Run it.
|
||||
$this->find_button(get_string('importbackupstage4action', 'backup'))->press();
|
||||
$this->wait();
|
||||
$this->wait(self::EXTENDED_TIMEOUT);
|
||||
|
||||
// Continue and redirect to 'to' course.
|
||||
$this->find_button(get_string('continue'))->press();
|
||||
@ -294,17 +305,22 @@ class behat_backup extends behat_base {
|
||||
// Settings.
|
||||
$this->fill_backup_restore_form($options);
|
||||
$this->find_button(get_string('restorestage4action', 'backup'))->press();
|
||||
$this->wait();
|
||||
|
||||
// Schema.
|
||||
$this->fill_backup_restore_form($options);
|
||||
$this->find_button(get_string('restorestage8action', 'backup'))->press();
|
||||
$this->wait();
|
||||
|
||||
// Review, no options here.
|
||||
$this->find_button(get_string('restorestage16action', 'backup'))->press();
|
||||
$this->wait(10);
|
||||
$this->wait();
|
||||
|
||||
// Last restore continue button, redirected to restore course after this.
|
||||
$this->find_button(get_string('restorestage32action', 'backup'))->press();
|
||||
|
||||
// Long wait when waiting for the restore to finish.
|
||||
$this->wait(self::EXTENDED_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -325,11 +341,15 @@ class behat_backup extends behat_base {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for the page to be loaded and the JS ready.
|
||||
$this->wait();
|
||||
|
||||
// If we find any of the provided options in the current form we should set the value.
|
||||
$datahash = $options->getRowsHash();
|
||||
foreach ($datahash as $locator => $value) {
|
||||
|
||||
try {
|
||||
// Using $this->find* to enforce stability over speed.
|
||||
$fieldnode = $this->find_field($locator);
|
||||
$field = behat_field_manager::get_form_field($fieldnode, $this->getSession());
|
||||
$field->set_value($value);
|
||||
@ -341,9 +361,9 @@ class behat_backup extends behat_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the DOM is ready.
|
||||
* Waits until the DOM and the page Javascript code is ready.
|
||||
*
|
||||
* @param int To override the default timeout
|
||||
* @param int $timeout The number of seconds that we wait.
|
||||
* @return void
|
||||
*/
|
||||
protected function wait($timeout = false) {
|
||||
@ -355,7 +375,8 @@ class behat_backup extends behat_base {
|
||||
if (!$timeout) {
|
||||
$timeout = self::TIMEOUT;
|
||||
}
|
||||
$this->getSession()->wait($timeout, '(document.readyState === "complete")');
|
||||
|
||||
$this->getSession()->wait($timeout * 1000, self::PAGE_READY_JS);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ Feature: Duplicate activities
|
||||
And I add a "Database" to section "1" and I fill the form with:
|
||||
| Name | Test database name |
|
||||
| Description | Test database description |
|
||||
And I open "Test database name" actions menu
|
||||
When I click on "Duplicate" "link" in the "Test database name" activity
|
||||
And I duplicate "Test database name" activity
|
||||
And I wait until section "1" is available
|
||||
And I open "Test database name" actions menu
|
||||
And I click on "Edit settings" "link" in the "Test database name" activity
|
||||
And I fill the moodle form with:
|
||||
|
@ -4,13 +4,10 @@ Feature: Award badges
|
||||
As an admin
|
||||
I need to add criteria to badges in the system
|
||||
|
||||
Background:
|
||||
Given I am on homepage
|
||||
And I log in as "admin"
|
||||
|
||||
@javascript
|
||||
Scenario: Award profile badge
|
||||
Given I expand "Site administration" node
|
||||
Given I log in as "admin"
|
||||
And I expand "Site administration" node
|
||||
And I expand "Badges" node
|
||||
And I follow "Add a new badge"
|
||||
And I fill the moodle form with:
|
||||
@ -46,6 +43,7 @@ Feature: Award badges
|
||||
| username | firstname | lastname | email |
|
||||
| teacher | teacher | 1 | teacher1@asd.com |
|
||||
| student | student | 1 | student1@asd.com |
|
||||
And I log in as "admin"
|
||||
And I expand "Site administration" node
|
||||
And I expand "Badges" node
|
||||
And I follow "Add a new badge"
|
||||
@ -89,7 +87,6 @@ Feature: Award badges
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I click on "//span[text()='Badges']" "xpath_element" in the "Administration" "block"
|
||||
@ -133,7 +130,6 @@ Feature: Award badges
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And I log out
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
| Enable completion tracking | 1 |
|
||||
@ -172,7 +168,6 @@ Feature: Award badges
|
||||
And I follow "Home"
|
||||
And I follow "Course 1"
|
||||
And I press "Mark as complete: Test assignment name"
|
||||
And I wait "2" seconds
|
||||
And I expand "My profile" node
|
||||
And I follow "My badges"
|
||||
Then I should see "Course Badge"
|
||||
@ -190,7 +185,6 @@ Feature: Award badges
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And I log out
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
| Enable completion tracking | 1 |
|
||||
|
@ -65,9 +65,6 @@ class behat_block_comments extends behat_base {
|
||||
|
||||
$this->find_link(get_string('savecomment'))->click();
|
||||
|
||||
// Wait for the AJAX request.
|
||||
$this->getSession()->wait(4 * 1000, false);
|
||||
|
||||
} else {
|
||||
|
||||
$commentstextarea = $this->find('css', '.block_comments form textarea', $exception);
|
||||
@ -103,7 +100,7 @@ class behat_block_comments extends behat_base {
|
||||
$deleteicon = $this->find('css', '.comment-delete a img', $deleteexception, $commentnode);
|
||||
$deleteicon->click();
|
||||
|
||||
// Wait for the AJAX request.
|
||||
// Wait for the animation to finish, in theory is just 1 sec, adding 4 just in case.
|
||||
$this->getSession()->wait(4 * 1000, false);
|
||||
}
|
||||
|
||||
|
@ -58,14 +58,11 @@ Feature: View my courses in navigation block
|
||||
And I should see "cat3" in the "Navigation" "block"
|
||||
And I should not see "cat2" in the "Navigation" "block"
|
||||
And I expand "cat3" node
|
||||
And I wait "2" seconds
|
||||
And I should see "cat31" in the "Navigation" "block"
|
||||
And I should see "cat33" in the "Navigation" "block"
|
||||
And I should not see "cat32" in the "Navigation" "block"
|
||||
And I expand "cat31" node
|
||||
And I wait "2" seconds
|
||||
And I should see "c31" in the "Navigation" "block"
|
||||
And I expand "cat33" node
|
||||
And I wait "2" seconds
|
||||
And I should see "c331" in the "Navigation" "block"
|
||||
And I should not see "c332" in the "Navigation" "block"
|
||||
|
@ -58,6 +58,20 @@ class behat_blocks extends behat_base {
|
||||
return $steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Docks a block. Editing mode should be previously enabled.
|
||||
*
|
||||
* @Given /^I dock "(?P<block_name_string>(?:[^"]|\\")*)" block$/
|
||||
* @param string $blockname
|
||||
* @return Given
|
||||
*/
|
||||
public function i_dock_block($blockname) {
|
||||
|
||||
// Looking for both title and alt.
|
||||
$xpath = "//input[@type='image'][@title='" . get_string('dockblock', 'block', $blockname) . "' or @alt='" . get_string('addtodock', 'block') . "']";
|
||||
return new Given('I click on " ' . $xpath . '" "xpath_element" in the "' . $this->escape($blockname) . '" "block"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a block's actions menu if it is not already opened.
|
||||
*
|
||||
|
@ -33,11 +33,9 @@ Feature: Comment on a blog entry
|
||||
And I follow "Comments (0)"
|
||||
When I fill in "content" with "$My own >nasty< \"string\"!"
|
||||
And I follow "Save comment"
|
||||
And I wait "4" seconds
|
||||
Then I should see "$My own >nasty< \"string\"!"
|
||||
And I fill in "content" with "Another $Nasty <string?>"
|
||||
And I follow "Save comment"
|
||||
And I wait "4" seconds
|
||||
And I should see "Comments (2)" in the ".comment-link" "css_element"
|
||||
|
||||
@javascript
|
||||
@ -53,8 +51,8 @@ Feature: Comment on a blog entry
|
||||
And I follow "Comments (0)"
|
||||
And I fill in "content" with "$My own >nasty< \"string\"!"
|
||||
And I follow "Save comment"
|
||||
And I wait "4" seconds
|
||||
When I click on ".comment-delete a" "css_element"
|
||||
# Waiting for the animation to finish.
|
||||
And I wait "4" seconds
|
||||
Then I should not see "$My own >nasty< \"string\"!"
|
||||
And I follow "Blog post from user 1"
|
||||
@ -73,5 +71,4 @@ Feature: Comment on a blog entry
|
||||
When I follow "Comments (0)"
|
||||
And I fill in "content" with "$My own >nasty< \"string\"!"
|
||||
And I follow "Save comment"
|
||||
And I wait "4" seconds
|
||||
Then I should see "$My own >nasty< \"string\"!"
|
||||
|
@ -37,7 +37,6 @@ Feature: Allow students to manually mark an activity as complete
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I press "Mark as complete: Test forum name"
|
||||
And I wait "3" seconds
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
|
@ -20,13 +20,16 @@ Feature: Restrict activity availability through date conditions
|
||||
And I set the following administration settings values:
|
||||
| Enable conditional access | 1 |
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
# Adding the page like this because id_available*_enabled needs to be clicked to trigger the action.
|
||||
And I add a "Assignment" to section "1"
|
||||
And I expand all fieldsets
|
||||
|
||||
@javascript
|
||||
Scenario: Show activity greyed-out to students when available from date is in future
|
||||
Given I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add a "Assignment" to section "1"
|
||||
Given I click on "id_availablefrom_enabled" "checkbox"
|
||||
And I fill the moodle form with:
|
||||
| Assignment name | Test assignment 1 |
|
||||
| Description | This assignment is restricted by date |
|
||||
@ -36,7 +39,6 @@ Feature: Restrict activity availability through date conditions
|
||||
| id_availablefrom_month | 12 |
|
||||
| id_availablefrom_year | 2050 |
|
||||
| id_showavailability | 1 |
|
||||
And I click on "id_availablefrom_enabled" "checkbox"
|
||||
And I press "Save and return to course"
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
@ -47,10 +49,7 @@ Feature: Restrict activity availability through date conditions
|
||||
|
||||
@javascript
|
||||
Scenario: Show activity hidden to students when available until date is in past
|
||||
Given I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add a "Assignment" to section "2"
|
||||
Given I click on "id_availableuntil_enabled" "checkbox"
|
||||
And I fill the moodle form with:
|
||||
| Assignment name | Test assignment 2 |
|
||||
| Description | This assignment is restricted by date |
|
||||
@ -60,7 +59,6 @@ Feature: Restrict activity availability through date conditions
|
||||
| id_availableuntil_month | 2 |
|
||||
| id_availableuntil_year | 2013 |
|
||||
| id_showavailability | 0 |
|
||||
And I click on "id_availableuntil_enabled" "checkbox"
|
||||
And I press "Save and return to course"
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
|
@ -29,13 +29,18 @@ Feature: Restrict activity availability through grade conditions
|
||||
| Description | Grade this assignment to revoke restriction on restricted assignment |
|
||||
| assignsubmission_onlinetext_enabled | 1 |
|
||||
| assignsubmission_file_enabled | 0 |
|
||||
And I add a "Page" to section "2" and I fill the form with:
|
||||
# Adding the page like this because id_availableform_enabled needs to be clicked to trigger the action.
|
||||
And I add a "Page" to section "2"
|
||||
And I expand all fieldsets
|
||||
And I click on "id_availablefrom_enabled" "checkbox"
|
||||
And I fill the moodle form with:
|
||||
| Name | Test page name |
|
||||
| Description | Restricted page, till grades in Grade assignment is at least 20% |
|
||||
| Page content | Test page contents |
|
||||
| id_conditiongradegroup_0_conditiongradeitemid | 2 |
|
||||
| id_conditiongradegroup_0_conditiongrademin | 20 |
|
||||
| id_showavailability | 1 |
|
||||
And I press "Save and return to course"
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
|
@ -29,14 +29,12 @@ Feature: Toggle activities groups mode from the course page
|
||||
Then "No groups (Click to change)" "link" should exists
|
||||
And "//a/child::img[contains(@src, 'groupn')]" "xpath_element" should exists
|
||||
And I click on "No groups (Click to change)" "link" in the "Test forum name" activity
|
||||
And I wait "3" seconds
|
||||
And "Separate groups (Click to change)" "link" should exists
|
||||
And "//a/child::img[contains(@src, 'groups')]" "xpath_element" should exists
|
||||
And I reload the page
|
||||
And "Separate groups (Click to change)" "link" should exists
|
||||
And "//a/child::img[contains(@src, 'groups')]" "xpath_element" should exists
|
||||
And I click on "Separate groups (Click to change)" "link" in the "Test forum name" activity
|
||||
And I wait "3" seconds
|
||||
And "Visible groups (Click to change)" "link" should exists
|
||||
And "//a/child::img[contains(@src, 'groupv')]" "xpath_element" should exists
|
||||
And I reload the page
|
||||
|
@ -40,8 +40,10 @@ Feature: Add activities to courses
|
||||
Scenario: Add an activity without the required fields
|
||||
When I add a "Database" to section "3" and I fill the form with:
|
||||
| Name | Test name |
|
||||
And I press "Save and return to course"
|
||||
Then I should see "Adding a new"
|
||||
And I should see "Required"
|
||||
And I press "Cancel"
|
||||
|
||||
Scenario: Add an activity to a course with Javascript disabled
|
||||
Then I should see "Add a resource to section 'Topic 1'"
|
||||
|
@ -67,15 +67,50 @@ class behat_course extends behat_base {
|
||||
* @return Given[]
|
||||
*/
|
||||
public function i_create_a_course_with(TableNode $table) {
|
||||
return array(
|
||||
|
||||
$steps = array(
|
||||
new Given('I go to the courses management page'),
|
||||
new Given('I should see the "'.get_string('categories').'" management page'),
|
||||
new Given('I click on category "'.get_string('miscellaneous').'" in the management interface'),
|
||||
new Given('I should see the "'.get_string('categoriesandcoures').'" management page'),
|
||||
new Given('I click on "'.get_string('createnewcourse').'" "link" in the "#course-listing" "css_element"'),
|
||||
new Given('I fill the moodle form with:', $table),
|
||||
new Given('I press "' . get_string('savechanges') . '"')
|
||||
new Given('I click on "'.get_string('createnewcourse').'" "link" in the "#course-listing" "css_element"')
|
||||
);
|
||||
|
||||
// If the course format is one of the fields we change how we
|
||||
// fill the form as we need to wait for the form to be set.
|
||||
$rowshash = $table->getRowsHash();
|
||||
$formatfieldrefs = array(get_string('format'), 'format', 'id_format');
|
||||
foreach ($formatfieldrefs as $fieldref) {
|
||||
if (!empty($rowshash[$fieldref])) {
|
||||
$formatfield = $fieldref;
|
||||
}
|
||||
}
|
||||
|
||||
// Setting the format separately.
|
||||
if (!empty($formatfield)) {
|
||||
|
||||
// Removing the format field from the TableNode.
|
||||
$rows = $table->getRows();
|
||||
$formatvalue = $rowshash[$formatfield];
|
||||
foreach ($rows as $key => $row) {
|
||||
if ($row[0] == $formatfield) {
|
||||
unset($rows[$key]);
|
||||
}
|
||||
}
|
||||
$table->setRows($rows);
|
||||
|
||||
// Adding a forced wait until editors are loaded as otherwise selenium sometimes tries clicks on the
|
||||
// format field when the editor is being rendered and the click misses the field coordinates.
|
||||
$steps[] = new Given('I wait until the editors are loaded');
|
||||
$steps[] = new Given('I select "' . $formatvalue . '" from "' . $formatfield . '"');
|
||||
$steps[] = new Given('I fill the moodle form with:', $table);
|
||||
} else {
|
||||
$steps[] = new Given('I fill the moodle form with:', $table);
|
||||
}
|
||||
|
||||
$steps[] = new Given('I press "' . get_string('savechanges') . '"');
|
||||
|
||||
return $steps;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -181,10 +216,7 @@ class behat_course extends behat_base {
|
||||
// Ensures the section exists.
|
||||
$xpath = $this->section_exists($sectionnumber);
|
||||
|
||||
return array(
|
||||
new Given('I click on "' . get_string('markthistopic') . '" "link" in the "' . $this->escape($xpath) . '" "xpath_element"'),
|
||||
new Given('I wait "2" seconds')
|
||||
);
|
||||
return new Given('I click on "' . get_string('markthistopic') . '" "link" in the "' . $this->escape($xpath) . '" "xpath_element"');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -199,10 +231,7 @@ class behat_course extends behat_base {
|
||||
// Ensures the section exists.
|
||||
$xpath = $this->section_exists($sectionnumber);
|
||||
|
||||
return array(
|
||||
new Given('I click on "' . get_string('markedthistopic') . '" "link" in the "' . $this->escape($xpath) . '" "xpath_element"'),
|
||||
new Given('I wait "2" seconds')
|
||||
);
|
||||
return new Given('I click on "' . get_string('markedthistopic') . '" "link" in the "' . $this->escape($xpath) . '" "xpath_element"');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -215,9 +244,9 @@ class behat_course extends behat_base {
|
||||
$showlink = $this->show_section_icon_exists($sectionnumber);
|
||||
$showlink->click();
|
||||
|
||||
// It requires time.
|
||||
if ($this->running_javascript()) {
|
||||
$this->getSession()->wait(5000, false);
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
$this->i_wait_until_section_is_available($sectionnumber);
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,9 +260,9 @@ class behat_course extends behat_base {
|
||||
$hidelink = $this->hide_section_icon_exists($sectionnumber);
|
||||
$hidelink->click();
|
||||
|
||||
// It requires time.
|
||||
if ($this->running_javascript()) {
|
||||
$this->getSession()->wait(5000, false);
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
$this->i_wait_until_section_is_available($sectionnumber);
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,6 +343,11 @@ class behat_course extends behat_base {
|
||||
|
||||
$sectionxpath = $this->section_exists($sectionnumber);
|
||||
|
||||
// Preventive in case there is any action in progress.
|
||||
// Adding it here because we are interacting (click) with
|
||||
// the elements, not necessary when we just find().
|
||||
$this->i_wait_until_section_is_available($sectionnumber);
|
||||
|
||||
// Section should be hidden.
|
||||
$exception = new ExpectationException('The section is not hidden', $this->getSession());
|
||||
$this->find('xpath', $sectionxpath . "[contains(concat(' ', normalize-space(@class), ' '), ' hidden ')]", $exception);
|
||||
@ -338,9 +372,12 @@ class behat_course extends behat_base {
|
||||
// Non-JS browsers can not click on img elements.
|
||||
if ($this->running_javascript()) {
|
||||
|
||||
// Expanding the actions menu.
|
||||
$actionsmenu = $this->find('css', "a[role='menuitem']", false, $activity);
|
||||
$actionsmenu->click();
|
||||
// Expanding the actions menu if it is not shown.
|
||||
$classes = array_flip(explode(' ', $activity->getAttribute('class')));
|
||||
if (empty($classes['action-menu-shown'])) {
|
||||
$actionsmenu = $this->find('css', "a[role='menuitem']", false, $activity);
|
||||
$actionsmenu->click();
|
||||
}
|
||||
|
||||
// To check that the visibility is not clickable we check the funcionality rather than the applied style.
|
||||
$visibilityiconnode = $this->find('css', 'a.editing_show img', false, $activity);
|
||||
@ -349,6 +386,17 @@ class behat_course extends behat_base {
|
||||
|
||||
// We ensure that we still see the show icon.
|
||||
$visibilityiconnode = $this->find('css', 'a.editing_show img', $visibilityexception, $activity);
|
||||
|
||||
// It is there only when running JS scenarios.
|
||||
if ($this->running_javascript()) {
|
||||
|
||||
// Collapse the actions menu if it is displayed.
|
||||
$classes = array_flip(explode(' ', $activity->getAttribute('class')));
|
||||
if (!empty($classes['action-menu-shown'])) {
|
||||
$actionsmenu = $this->find('css', "a[role='menuitem']", false, $activity);
|
||||
$actionsmenu->click();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -543,8 +591,7 @@ class behat_course extends behat_base {
|
||||
$activity = $this->escape($activityname);
|
||||
return array(
|
||||
new Given('I click on "' . get_string('edittitle') . '" "link" in the "' . $activity .'" activity'),
|
||||
new Given('I fill in "title" with "' . $this->escape($newactivityname) . chr(10) . '"'),
|
||||
new Given('I wait "2" seconds')
|
||||
new Given('I fill in "title" with "' . $this->escape($newactivityname) . chr(10) . '"')
|
||||
);
|
||||
}
|
||||
|
||||
@ -572,6 +619,30 @@ class behat_course extends behat_base {
|
||||
return new Given('I click on "a[role=\'menuitem\']" "css_element" in the "' . $this->escape($activityname) . '" activity');
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes an activity actions menu if it is not already closed.
|
||||
*
|
||||
* @Given /^I close "(?P<activity_name_string>(?:[^"]|\\")*)" actions menu$/
|
||||
* @throws DriverException The step is not available when Javascript is disabled
|
||||
* @param string $activityname
|
||||
* @return Given
|
||||
*/
|
||||
public function i_close_actions_menu($activityname) {
|
||||
|
||||
if (!$this->running_javascript()) {
|
||||
throw new DriverException('Activities actions menu not available when Javascript is disabled');
|
||||
}
|
||||
|
||||
// If it is already closed we do nothing.
|
||||
$activitynode = $this->get_activity_node($activityname);
|
||||
$classes = array_flip(explode(' ', $activitynode->getAttribute('class')));
|
||||
if (empty($classes['action-menu-shown'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
return new Given('I click on "a[role=\'menuitem\']" "css_element" in the "' . $this->escape($activityname) . '" activity');
|
||||
}
|
||||
|
||||
/**
|
||||
* Indents to the right the activity or resource specified by it's name. Editing mode should be on.
|
||||
*
|
||||
@ -588,10 +659,6 @@ class behat_course extends behat_base {
|
||||
}
|
||||
$steps[] = new Given('I click on "' . get_string('moveright') . '" "link" in the "' . $activity . '" activity');
|
||||
|
||||
if ($this->running_javascript()) {
|
||||
$steps[] = new Given('I wait "2" seconds');
|
||||
}
|
||||
|
||||
return $steps;
|
||||
}
|
||||
|
||||
@ -611,10 +678,6 @@ class behat_course extends behat_base {
|
||||
}
|
||||
$steps[] = new Given('I click on "' . get_string('moveleft') . '" "link" in the "' . $activity . '" activity');
|
||||
|
||||
if ($this->running_javascript()) {
|
||||
$steps[] = new Given('I wait "2" seconds');
|
||||
}
|
||||
|
||||
return $steps;
|
||||
|
||||
}
|
||||
@ -640,8 +703,6 @@ class behat_course extends behat_base {
|
||||
|
||||
$this->getSession()->getDriver()->getWebDriverSession()->accept_alert();
|
||||
|
||||
$this->getSession()->wait(2 * 1000, false);
|
||||
|
||||
} else {
|
||||
|
||||
// With JS disabled.
|
||||
@ -668,10 +729,7 @@ class behat_course extends behat_base {
|
||||
$steps[] = new Given('I open "' . $activity . '" actions menu');
|
||||
}
|
||||
$steps[] = new Given('I click on "' . get_string('duplicate') . '" "link" in the "' . $activity . '" activity');
|
||||
if ($this->running_javascript()) {
|
||||
// Temporary wait until MDL-41030 lands.
|
||||
$steps[] = new Given('I wait "4" seconds');
|
||||
} else {
|
||||
if (!$this->running_javascript()) {
|
||||
$steps[] = new Given('I press "' . get_string('continue') .'"');
|
||||
$steps[] = new Given('I press "' . get_string('duplicatecontcourse') .'"');
|
||||
}
|
||||
@ -691,12 +749,22 @@ class behat_course extends behat_base {
|
||||
$steps = array();
|
||||
|
||||
$activity = $this->escape($activityname);
|
||||
$activityliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($activityname);
|
||||
|
||||
if ($this->running_javascript()) {
|
||||
$steps[] = new Given('I duplicate "' . $activity . '" activity');
|
||||
|
||||
// We wait until the AJAX request finishes and the section is visible again.
|
||||
$hiddenlightboxxpath = "//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')][contains(., $activityliteral)]" .
|
||||
"/ancestor::li[contains(concat(' ', normalize-space(@class), ' '), ' section ')]" .
|
||||
"/descendant::div[contains(concat(' ', @class, ' '), ' lightbox ')][contains(@style, 'display: none')]";
|
||||
$steps[] = new Given('I wait until the page is ready');
|
||||
$steps[] = new Given('I wait until "' . $this->escape($hiddenlightboxxpath) .'" "xpath_element" exists');
|
||||
|
||||
// Close the original activity actions menu.
|
||||
$steps[] = new Given('I close "' . $activity . '" actions menu');
|
||||
|
||||
// Determine the future new activity xpath from the former one.
|
||||
$activityliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($activityname);
|
||||
$duplicatedxpath = "//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')][contains(., $activityliteral)]" .
|
||||
"/following-sibling::li";
|
||||
$duplicatedactionsmenuxpath = $duplicatedxpath . "/descendant::a[@role='menuitem']";
|
||||
@ -717,6 +785,32 @@ class behat_course extends behat_base {
|
||||
return $steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the section is available to interact with it. Useful when the section is performing an action and the section is overlayed with a loading layout.
|
||||
*
|
||||
* Using the protected method as this method will be usually
|
||||
* called by other methods which are not returning a set of
|
||||
* steps and performs the actions directly, so it would not
|
||||
* be executed if it returns another step.
|
||||
*
|
||||
* Hopefully we would not require test writers to use this step
|
||||
* and we will manage it from other step definitions.
|
||||
*
|
||||
* @Given /^I wait until section "(?P<section_number>\d+)" is available$/
|
||||
* @param int $sectionnumber
|
||||
* @return void
|
||||
*/
|
||||
public function i_wait_until_section_is_available($sectionnumber) {
|
||||
|
||||
// Looks for a hidden lightbox or a non-existent lightbox in that section.
|
||||
$sectionxpath = $this->section_exists($sectionnumber);
|
||||
$hiddenlightboxxpath = $sectionxpath . "/descendant::div[contains(concat(' ', @class, ' '), ' lightbox ')][contains(@style, 'display: none')]" .
|
||||
" | " .
|
||||
$sectionxpath . "[count(child::div[contains(@class, 'lightbox')]) = 0]";
|
||||
|
||||
$this->ensure_element_exists($hiddenlightboxxpath, 'xpath_element');
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks on the specified element of the activity. You should be in the course page with editing mode turned on.
|
||||
*
|
||||
@ -882,6 +976,18 @@ class behat_course extends behat_base {
|
||||
return $this->find('xpath', $xpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the activity instance name from the activity node.
|
||||
*
|
||||
* @throws ElementNotFoundException
|
||||
* @param NodeElement $activitynode
|
||||
* @return string
|
||||
*/
|
||||
protected function get_activity_name($activitynode) {
|
||||
$instancenamenode = $this->find('xpath', "//span[contains(concat(' ', normalize-space(@class), ' '), ' instancename ')]", false, $activitynode);
|
||||
return $instancenamenode->getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the user can edit the course contents or not.
|
||||
*
|
||||
|
@ -1,4 +1,4 @@
|
||||
@core @core_course @test
|
||||
@core @core_course
|
||||
Feature: Course category management interface performs as expected
|
||||
In order to test JS enhanced display of categories and subcategories.
|
||||
As a moodle admin
|
||||
|
@ -1,4 +1,4 @@
|
||||
@core @core_course
|
||||
@core @core_course @_alerts
|
||||
Feature: Course activity controls works as expected
|
||||
In order to manage my course's activities
|
||||
As a teacher
|
||||
@ -59,11 +59,16 @@ Feature: Course activity controls works as expected
|
||||
And I click on "Edit settings" "link" in the "Test forum name 1" activity
|
||||
And I should see "Updating Forum"
|
||||
And I should see "Display description on course page"
|
||||
And I press "Save and return to course"
|
||||
And I fill the moodle form with:
|
||||
| Forum name | Just to check that I can edit the name |
|
||||
| Description | Just to check that I can edit the description |
|
||||
| Display description on course page | 1 |
|
||||
And I click on "Cancel" "button"
|
||||
And "#section-2" "css_element" <should_see_other_sections> exists
|
||||
And I open "Test forum name 1" actions menu
|
||||
And I click on "Hide" "link" in the "Test forum name 1" activity
|
||||
And "#section-2" "css_element" <should_see_other_sections> exists
|
||||
And I close "Test forum name 1" actions menu
|
||||
And I duplicate "Test forum name 2" activity editing the new copy with:
|
||||
| Forum name | Edited test forum name 2 |
|
||||
And "#section-2" "css_element" <should_see_other_sections> exists
|
||||
|
@ -91,7 +91,7 @@ function UpdatableMembersCombo(wwwRoot, courseId) {
|
||||
this.courseId = courseId;
|
||||
|
||||
this.connectCallback = {
|
||||
success: function(o) {
|
||||
success: function(t, o) {
|
||||
|
||||
if (o.responseText !== undefined) {
|
||||
var selectEl = document.getElementById("members");
|
||||
@ -124,7 +124,7 @@ function UpdatableMembersCombo(wwwRoot, courseId) {
|
||||
removeLoaderImgs("membersloader", "memberslabel");
|
||||
},
|
||||
|
||||
failure: function(o) {
|
||||
failure: function() {
|
||||
removeLoaderImgs("membersloader", "memberslabel");
|
||||
}
|
||||
|
||||
@ -185,9 +185,13 @@ UpdatableMembersCombo.prototype.refreshMembers = function () {
|
||||
|
||||
if(singleSelection) {
|
||||
var sUrl = this.wwwRoot+"/group/index.php?id="+this.courseId+"&group="+groupId+"&act_ajax_getmembersingroup";
|
||||
var callback = this.connectCallback;
|
||||
YUI().use('yui2-connection', function (Y) {
|
||||
Y.YUI2.util.Connect.asyncRequest("GET", sUrl, callback, null);
|
||||
var self = this;
|
||||
YUI().use('io', function (Y) {
|
||||
Y.io(sUrl, {
|
||||
method: 'GET',
|
||||
context: this,
|
||||
on: self.connectCallback
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -271,4 +275,4 @@ function init_add_remove_members_page(Y) {
|
||||
|
||||
addselect = document.getElementById('addselect');
|
||||
addselect.onchange = updateUserSummary;
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ class behat_groups extends behat_base {
|
||||
$this->find_button(get_string('adduserstogroup', 'group'))->click();
|
||||
|
||||
// Wait for add/remove members page to be loaded.
|
||||
$this->getSession()->wait(self::TIMEOUT, '(document.readyState === "complete")');
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
|
||||
// Getting the option and selecting it.
|
||||
$select = $this->find_field('addselect');
|
||||
@ -80,7 +80,7 @@ class behat_groups extends behat_base {
|
||||
$this->find_button(get_string('add'))->click();
|
||||
|
||||
// Wait for the page to load.
|
||||
$this->getSession()->wait(self::TIMEOUT, '(document.readyState === "complete")');
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
|
||||
// Returning to the main groups page.
|
||||
$this->find_button(get_string('backtogroups', 'group'))->click();
|
||||
|
@ -40,12 +40,10 @@ Feature: Organize students into groups
|
||||
And I add "student2" user to "Group 2" group
|
||||
And I add "student3" user to "Group 2" group
|
||||
Then I select "Group 1 (2)" from "groups"
|
||||
And I wait "5" seconds
|
||||
And the "members" select box should contain "Student 0"
|
||||
And the "members" select box should contain "Student 1"
|
||||
And the "members" select box should not contain "Student 2"
|
||||
And I select "Group 2 (2)" from "groups"
|
||||
And I wait "5" seconds
|
||||
And the "members" select box should contain "Student 2"
|
||||
And the "members" select box should contain "Student 3"
|
||||
And the "members" select box should not contain "Student 0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
@core @core_group
|
||||
@core @core_group @_only_local
|
||||
Feature: Importing of groups and groupings
|
||||
In order to import groups and grouping
|
||||
As a teacher
|
||||
|
@ -55,7 +55,17 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
|
||||
/**
|
||||
* The timeout for each Behat step (load page, wait for an element to load...).
|
||||
*/
|
||||
const TIMEOUT = 6;
|
||||
const TIMEOUT = 3;
|
||||
|
||||
/**
|
||||
* And extended timeout for specific cases.
|
||||
*/
|
||||
const EXTENDED_TIMEOUT = 10;
|
||||
|
||||
/**
|
||||
* The JS code to check that the page is ready.
|
||||
*/
|
||||
const PAGE_READY_JS = '(M && M.util && M.util.pending_js && !Boolean(M.util.pending_js.length)) && (document.readyState === "complete")';
|
||||
|
||||
/**
|
||||
* Locates url, based on provided path.
|
||||
@ -420,4 +430,194 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
|
||||
return get_class($this->getSession()->getDriver()) !== 'Behat\Mink\Driver\GoutteDriver';
|
||||
}
|
||||
|
||||
/**
|
||||
* Spins around an element until it exists
|
||||
*
|
||||
* @throws ExpectationException
|
||||
* @param string $element
|
||||
* @param string $selectortype
|
||||
* @return void
|
||||
*/
|
||||
protected function ensure_element_exists($element, $selectortype) {
|
||||
|
||||
// Getting the behat selector & locator.
|
||||
list($selector, $locator) = $this->transform_selector($selectortype, $element);
|
||||
|
||||
// Exception if it timesout and the element is still there.
|
||||
$msg = 'The "' . $element . '" element does not exist and should exist';
|
||||
$exception = new ExpectationException($msg, $this->getSession());
|
||||
|
||||
// It will stop spinning once the find() method returns true.
|
||||
$this->spin(
|
||||
function($context, $args) {
|
||||
// We don't use behat_base::find as it is already spinning.
|
||||
if ($context->getSession()->getPage()->find($args['selector'], $args['locator'])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
array('selector' => $selector, 'locator' => $locator),
|
||||
self::EXTENDED_TIMEOUT,
|
||||
$exception,
|
||||
true
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Spins until the element does not exist
|
||||
*
|
||||
* @throws ExpectationException
|
||||
* @param string $element
|
||||
* @param string $selectortype
|
||||
* @return void
|
||||
*/
|
||||
protected function ensure_element_does_not_exist($element, $selectortype) {
|
||||
|
||||
// Getting the behat selector & locator.
|
||||
list($selector, $locator) = $this->transform_selector($selectortype, $element);
|
||||
|
||||
// Exception if it timesout and the element is still there.
|
||||
$msg = 'The "' . $element . '" element exists and should not exist';
|
||||
$exception = new ExpectationException($msg, $this->getSession());
|
||||
|
||||
// It will stop spinning once the find() method returns false.
|
||||
$this->spin(
|
||||
function($context, $args) {
|
||||
// We don't use behat_base::find() as we are already spinning.
|
||||
if (!$context->getSession()->getPage()->find($args['selector'], $args['locator'])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
array('selector' => $selector, 'locator' => $locator),
|
||||
self::EXTENDED_TIMEOUT,
|
||||
$exception,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the provided node is visible and we can interact with it.
|
||||
*
|
||||
* @throws ExpectationException
|
||||
* @param NodeElement $node
|
||||
* @return void Throws an exception if it times out without the element being visible
|
||||
*/
|
||||
protected function ensure_node_is_visible($node) {
|
||||
|
||||
if (!$this->running_javascript()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Exception if it timesout and the element is still there.
|
||||
$msg = 'The "' . $node->getXPath() . '" xpath node is not visible and it should be visible';
|
||||
$exception = new ExpectationException($msg, $this->getSession());
|
||||
|
||||
// It will stop spinning once the isVisible() method returns true.
|
||||
$this->spin(
|
||||
function($context, $args) {
|
||||
if ($args->isVisible()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
$node,
|
||||
self::EXTENDED_TIMEOUT,
|
||||
$exception,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the provided element is visible and we can interact with it.
|
||||
*
|
||||
* Returns the node in case other actions are interested in using it.
|
||||
*
|
||||
* @throws ExpectationException
|
||||
* @param string $element
|
||||
* @param string $selectortype
|
||||
* @return NodeElement Throws an exception if it times out without being visible
|
||||
*/
|
||||
protected function ensure_element_is_visible($element, $selectortype) {
|
||||
|
||||
if (!$this->running_javascript()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$node = $this->get_selected_node($selectortype, $element);
|
||||
$this->ensure_node_is_visible($node);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that all the page's editors are loaded.
|
||||
*
|
||||
* This method is expensive as it waits for .mceEditor CSS
|
||||
* so use with caution and only where there will be editors.
|
||||
*
|
||||
* @throws ElementNotFoundException
|
||||
* @throws ExpectationException
|
||||
* @return void
|
||||
*/
|
||||
protected function ensure_editors_are_loaded() {
|
||||
|
||||
if (!$this->running_javascript()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are no editors we don't need to wait.
|
||||
try {
|
||||
$this->find('css', '.mceEditor');
|
||||
} catch (ElementNotFoundException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Exception if it timesout and the element is not appearing.
|
||||
$msg = 'The editors are not completely loaded';
|
||||
$exception = new ExpectationException($msg, $this->getSession());
|
||||
|
||||
// Here we know that there are .mceEditor editors in the page and we will
|
||||
// probably need to interact with them, if we use tinyMCE JS var before
|
||||
// it exists it will throw an exception and we want to catch it until all
|
||||
// the page's editors are ready to interact with them.
|
||||
$this->spin(
|
||||
function($context) {
|
||||
|
||||
// It may return 0 if tinyMCE is loaded but not the instances, so we just loop again.
|
||||
$neditors = $context->getSession()->evaluateScript('return tinyMCE.editors.length;');
|
||||
if ($neditors == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// It may be there but not ready.
|
||||
$iframeready = $context->getSession()->evaluateScript('
|
||||
var readyeditors = new Array;
|
||||
for (editorid in tinyMCE.editors) {
|
||||
if (tinyMCE.editors[editorid].getDoc().readyState === "complete") {
|
||||
readyeditors[editorid] = editorid;
|
||||
}
|
||||
}
|
||||
if (tinyMCE.editors.length === readyeditors.length) {
|
||||
return "complete";
|
||||
}
|
||||
return "";
|
||||
');
|
||||
|
||||
// Now we know that the editors are there.
|
||||
if ($iframeready) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Loop again if it is not ready.
|
||||
return false;
|
||||
},
|
||||
false,
|
||||
self::EXTENDED_TIMEOUT,
|
||||
$exception,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -93,6 +93,7 @@ class behat_files extends behat_base {
|
||||
$classname = 'fp-file-' . $action;
|
||||
$button = $this->find('css', '.moodle-dialogue-focused button.' . $classname, $exception);
|
||||
|
||||
$this->ensure_node_is_visible($button);
|
||||
$button->click();
|
||||
}
|
||||
|
||||
@ -148,13 +149,14 @@ class behat_files extends behat_base {
|
||||
$locatorprefix .
|
||||
"//descendant::*[self::div | self::a][contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')]" .
|
||||
"[normalize-space(.)=$name]" .
|
||||
"//descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-thumbnail ')]",
|
||||
"//descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-filename-field ')]",
|
||||
false,
|
||||
$containernode
|
||||
);
|
||||
}
|
||||
|
||||
// Click opens the contextual menu when clicking on files.
|
||||
$this->ensure_node_is_visible($node);
|
||||
$node->click();
|
||||
}
|
||||
|
||||
@ -179,6 +181,7 @@ class behat_files extends behat_base {
|
||||
// Otherwise should be a single-file filepicker form element.
|
||||
$add = $this->find('css', 'input.fp-btn-choose', $exception, $filemanagernode);
|
||||
}
|
||||
$this->ensure_node_is_visible($add);
|
||||
$add->click();
|
||||
|
||||
// Getting the repository link and opening it.
|
||||
@ -197,12 +200,16 @@ class behat_files extends behat_base {
|
||||
);
|
||||
|
||||
// Selecting the repo.
|
||||
$this->ensure_node_is_visible($repositorylink);
|
||||
$repositorylink->click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the file manager modal windows are closed.
|
||||
*
|
||||
* This method is not used by any of our step definitions,
|
||||
* keeping it here for users already using it.
|
||||
*
|
||||
* @throws ExpectationException
|
||||
* @return void
|
||||
*/
|
||||
@ -220,6 +227,9 @@ class behat_files extends behat_base {
|
||||
/**
|
||||
* Checks that the file manager contents are not being updated.
|
||||
*
|
||||
* This method is not used by any of our step definitions,
|
||||
* keeping it here for users already using it.
|
||||
*
|
||||
* @throws ExpectationException
|
||||
* @param NodeElement $filepickernode The file manager DOM node
|
||||
* @return void
|
||||
@ -243,9 +253,6 @@ class behat_files extends behat_base {
|
||||
$exception,
|
||||
$filepickernode
|
||||
);
|
||||
|
||||
// After removing the class FileManagerHelper.view_files() performs other actions.
|
||||
$this->getSession()->wait(4 * 1000, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -48,19 +48,38 @@ class behat_form_editor extends behat_form_field {
|
||||
*/
|
||||
public function set_value($value) {
|
||||
|
||||
// Get tinyMCE editor id if it exists.
|
||||
if ($editorid = $this->get_editor_id()) {
|
||||
$lastexception = null;
|
||||
|
||||
// Set the value to the iframe and save it to the textarea.
|
||||
$this->session->executeScript('
|
||||
tinyMCE.get("'.$editorid.'").setContent("' . $value . '");
|
||||
tinyMCE.get("'.$editorid.'").save();
|
||||
');
|
||||
// We want the editor to be ready, otherwise the value can not
|
||||
// be set and an exception is thrown.
|
||||
for ($i = 0; $i < behat_base::EXTENDED_TIMEOUT; $i++) {
|
||||
try {
|
||||
// Get tinyMCE editor id if it exists.
|
||||
if ($editorid = $this->get_editor_id()) {
|
||||
|
||||
} else {
|
||||
// Set the value to a textarea otherwise.
|
||||
parent::set_value($value);
|
||||
// Set the value to the iframe and save it to the textarea.
|
||||
$this->session->executeScript('
|
||||
tinyMCE.get("'.$editorid.'").setContent("' . $value . '");
|
||||
tinyMCE.get("'.$editorid.'").save();
|
||||
');
|
||||
|
||||
} else {
|
||||
// Set the value to a textarea otherwise.
|
||||
parent::set_value($value);
|
||||
}
|
||||
return;
|
||||
|
||||
} catch (Exception $e) {
|
||||
// Catching any kind of exception and ignoring it until times out.
|
||||
$lastexception = $e;
|
||||
|
||||
// Waiting 0.1 seconds.
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
|
||||
// If it is not available we throw the last exception.
|
||||
throw $lastexception;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,14 +89,45 @@ class behat_form_editor extends behat_form_field {
|
||||
*/
|
||||
public function get_value() {
|
||||
|
||||
// Get tinyMCE editor id if it exists.
|
||||
if ($editorid = $this->get_editor_id()) {
|
||||
// Can be be a string value or an exception depending whether the editor loads or not.
|
||||
$lastoutcome = '';
|
||||
|
||||
// Save the current iframe value in case default value has been edited.
|
||||
$this->session->executeScript('tinyMCE.get("'.$editorid.'").save();');
|
||||
// We want the editor to be ready to return the correct value, sometimes the
|
||||
// page loads too fast and the returned value may be '' if the editor didn't
|
||||
// have enough time to load completely despite having a different value.
|
||||
for ($i = 0; $i < behat_base::EXTENDED_TIMEOUT; $i++) {
|
||||
try {
|
||||
|
||||
// Get tinyMCE editor id if it exists.
|
||||
if ($editorid = $this->get_editor_id()) {
|
||||
|
||||
// Save the current iframe value in case default value has been edited.
|
||||
$this->session->executeScript('tinyMCE.get("'.$editorid.'").save();');
|
||||
}
|
||||
|
||||
$lastoutcome = $this->field->getValue();
|
||||
|
||||
// We only want to wait until it times out if the value is empty.
|
||||
if ($lastoutcome != '') {
|
||||
return $lastoutcome;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
// Catching any kind of exception and ignoring it until times out.
|
||||
$lastoutcome = $e;
|
||||
|
||||
// Waiting 0.1 seconds.
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->field->getValue();
|
||||
// If it is not available we throw the last exception.
|
||||
if (is_a($lastoutcome, 'Exception')) {
|
||||
throw $lastoutcome;
|
||||
}
|
||||
|
||||
// Return the value if there are no exceptions it will be '' at this point
|
||||
return $lastoutcome;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,7 +137,7 @@ class behat_form_editor extends behat_form_field {
|
||||
* can not execute Javascript, also some Moodle settings disables the HTML
|
||||
* editor.
|
||||
*
|
||||
* @return mixed The id of the editor of false if is not available
|
||||
* @return mixed The id of the editor of false if it is not available
|
||||
*/
|
||||
protected function get_editor_id() {
|
||||
|
||||
@ -95,7 +145,7 @@ class behat_form_editor extends behat_form_field {
|
||||
try {
|
||||
$available = $this->session->evaluateScript('return (typeof tinyMCE != "undefined")');
|
||||
|
||||
// Also checking that it exist a tinyMCE editor for the requested field.
|
||||
// Also checking that it exists a tinyMCE editor for the requested field.
|
||||
$editorid = $this->field->getAttribute('id');
|
||||
$available = $this->session->evaluateScript('return (typeof tinyMCE.get("'.$editorid.'") != "undefined")');
|
||||
|
||||
|
@ -137,6 +137,7 @@ class behat_form_field {
|
||||
$classname = 'behat_form_select';
|
||||
|
||||
} else {
|
||||
// We can not provide a closer field type.
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -154,4 +155,24 @@ class behat_form_field {
|
||||
return get_class($this->session->getDriver()) !== 'Behat\Mink\Driver\GoutteDriver';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the field internal id used by selenium wire protocol.
|
||||
*
|
||||
* Only available when running_javascript().
|
||||
*
|
||||
* @throws coding_exception
|
||||
* @return int
|
||||
*/
|
||||
protected function get_internal_field_id() {
|
||||
|
||||
if (!$this->running_javascript()) {
|
||||
throw new coding_exception('You can only get an internal ID using the selenium driver.');
|
||||
}
|
||||
|
||||
return $this->session->
|
||||
getDriver()->
|
||||
getWebDriverSession()->
|
||||
element('xpath', $this->field->getXPath())->
|
||||
getID();
|
||||
}
|
||||
}
|
||||
|
@ -40,40 +40,82 @@ class behat_form_select extends behat_form_field {
|
||||
/**
|
||||
* Sets the value of a single select.
|
||||
*
|
||||
* Seems an easy select, but there are lots of combinations
|
||||
* of browsers and operative systems and each one manages the
|
||||
* autosubmits and the multiple option selects in a diferent way.
|
||||
*
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function set_value($value) {
|
||||
|
||||
// In some browsers we select an option and it triggers all the
|
||||
// autosubmits and works as expected but not in all of them, so we
|
||||
// try to catch all the possibilities to make this function work as
|
||||
// expected.
|
||||
|
||||
// Get the internal id of the element we are going to click.
|
||||
// This kind of internal IDs are only available in the selenium wire
|
||||
// protocol, so only available using selenium drivers, phantomjs and family.
|
||||
if ($this->running_javascript()) {
|
||||
$currentelementid = $this->get_internal_field_id();
|
||||
}
|
||||
|
||||
// Here we select an option.
|
||||
$this->field->selectOption($value);
|
||||
|
||||
// Adding a click as Selenium requires it to fire some JS events.
|
||||
if ($this->running_javascript()) {
|
||||
// With JS disabled this is enough and we finish here.
|
||||
if (!$this->running_javascript()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// In some browsers the selectOption actions can perform a page reload
|
||||
// so we need to ensure the element is still available to continue interacting
|
||||
// with it. We don't wait here.
|
||||
if (!$this->session->getDriver()->find($this->field->getXpath())) {
|
||||
// With JS enabled we add more clicks as some selenium
|
||||
// drivers requires it to fire JS events.
|
||||
|
||||
// In some browsers the selectOption actions can perform a form submit or reload page
|
||||
// so we need to ensure the element is still available to continue interacting
|
||||
// with it. We don't wait here.
|
||||
$selectxpath = $this->field->getXpath();
|
||||
if (!$this->session->getDriver()->find($selectxpath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We also check the selenium internal element id, if it have changed
|
||||
// we are dealing with an autosubmit that was already executed, and we don't to
|
||||
// execute anything else as the action we wanted was already performed.
|
||||
if ($currentelementid != $this->get_internal_field_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We also check that the option is still there. We neither wait.
|
||||
$valueliteral = $this->session->getSelectorsHandler()->xpathLiteral($value);
|
||||
$optionxpath = $selectxpath . "/descendant::option[(./@value=$valueliteral or normalize-space(.)=$valueliteral)]";
|
||||
if (!$this->session->getDriver()->find($optionxpath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Single select sometimes needs an extra click in the option.
|
||||
if (!$this->field->hasAttribute('multiple')) {
|
||||
|
||||
// Using the driver direcly because Element methods are messy when dealing
|
||||
// with elements inside containers.
|
||||
$optionnodes = $this->session->getDriver()->find($optionxpath);
|
||||
if ($optionnodes) {
|
||||
current($optionnodes)->click();
|
||||
}
|
||||
|
||||
} else {
|
||||
// Multiple ones needs the click in the select.
|
||||
$this->field->click();
|
||||
|
||||
// We ensure that the option is still there.
|
||||
if (!$this->session->getDriver()->find($optionxpath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Single select needs an extra click in the option.
|
||||
if (!$this->field->hasAttribute('multiple')) {
|
||||
|
||||
$value = $this->session->getSelectorsHandler()->xpathLiteral($value);
|
||||
|
||||
// Using the driver direcly because Element methods are messy when dealing
|
||||
// with elements inside containers.
|
||||
$optionxpath = $this->field->getXpath() .
|
||||
"/descendant::option[(./@value=$value or normalize-space(.)=$value)]";
|
||||
$optionnodes = $this->session->getDriver()->find($optionxpath);
|
||||
if ($optionnodes) {
|
||||
current($optionnodes)->click();
|
||||
}
|
||||
|
||||
} else {
|
||||
// Multiple ones needs the click in the select.
|
||||
$this->field->click();
|
||||
}
|
||||
// Repeating the select as some drivers (chrome that I know) are moving
|
||||
// to another option after the general select field click above.
|
||||
$this->field->selectOption($value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,8 @@ M.editor_tinymce.init_editor = function(Y, editorid, options) {
|
||||
};
|
||||
|
||||
M.editor_tinymce.initialised = true;
|
||||
M.util.js_pending('editors');
|
||||
options.oninit = "M.editor_tinymce.init_callback";
|
||||
}
|
||||
|
||||
M.editor_tinymce.editor_options[editorid] = options;
|
||||
@ -86,6 +88,10 @@ M.editor_tinymce.init_editor = function(Y, editorid, options) {
|
||||
}
|
||||
};
|
||||
|
||||
M.editor_tinymce.init_callback = function() {
|
||||
M.util.js_complete('editors');
|
||||
}
|
||||
|
||||
M.editor_tinymce.init_filepicker = function(Y, editorid, options) {
|
||||
M.editor_tinymce.filepicker_options[editorid] = options;
|
||||
};
|
||||
|
@ -45,6 +45,7 @@ Feature: Add or remove items from the TinyMCE editor toolbar
|
||||
Given I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
When I add a "Database" to section "1"
|
||||
And I wait until "#id_introeditor_tbl" "css_element" exists
|
||||
Then "#id_introeditor_tbl .mce_bold" "css_element" should exists
|
||||
And "#id_introeditor_tbl .mce_anchor" "css_element" should not exists
|
||||
And I press "Cancel"
|
||||
|
@ -755,6 +755,76 @@ M.util.init_block_hider = function(Y, config) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @var pending_js - The keys are the list of all pending js actions.
|
||||
* @type Object
|
||||
*/
|
||||
M.util.pending_js = [];
|
||||
M.util.complete_js = [];
|
||||
|
||||
/**
|
||||
* Register any long running javascript code with a unique identifier.
|
||||
* Should be followed with a call to js_complete with a matching
|
||||
* idenfitier when the code is complete. May also be called with no arguments
|
||||
* to test if there is any js calls pending. This is relied on by behat so that
|
||||
* it can wait for all pending updates before interacting with a page.
|
||||
* @param String uniqid - optional, if provided,
|
||||
* registers this identifier until js_complete is called.
|
||||
* @return boolean - True if there is any pending js.
|
||||
*/
|
||||
M.util.js_pending = function(uniqid) {
|
||||
if (uniqid !== false) {
|
||||
M.util.pending_js.push(uniqid);
|
||||
}
|
||||
|
||||
return M.util.pending_js.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register listeners for Y.io start/end so we can wait for them in behat.
|
||||
*/
|
||||
M.util.js_watch_io = function() {
|
||||
YUI.add('moodle-core-io', function(Y) {
|
||||
Y.on('io:start', function(id) {
|
||||
M.util.js_pending('io:' + id);
|
||||
});
|
||||
Y.on('io:end', function(id) {
|
||||
M.util.js_complete('io:' + id);
|
||||
});
|
||||
});
|
||||
YUI.applyConfig({
|
||||
modules: {
|
||||
'moodle-core-io': {
|
||||
after: ['io-base']
|
||||
},
|
||||
'io-base': {
|
||||
requires: ['moodle-core-io'],
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// Start this asap.
|
||||
M.util.js_pending('init');
|
||||
M.util.js_watch_io();
|
||||
|
||||
/**
|
||||
* Unregister any long running javascript code by unique identifier.
|
||||
* This function should form a matching pair with js_pending
|
||||
*
|
||||
* @param String uniqid - required, unregisters this identifier
|
||||
* @return boolean - True if there is any pending js.
|
||||
*/
|
||||
M.util.js_complete = function(uniqid) {
|
||||
var index = M.util.pending_js.indexOf(uniqid);
|
||||
if (index >= 0) {
|
||||
M.util.complete_js.push(M.util.pending_js.splice(index, 1));
|
||||
}
|
||||
|
||||
return M.util.pending_js.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a string registered in advance for usage in JavaScript
|
||||
*
|
||||
|
@ -1043,14 +1043,18 @@ class page_requirements_manager {
|
||||
public function js_init_code($jscode, $ondomready = false, array $module = null) {
|
||||
$jscode = trim($jscode, " ;\n"). ';';
|
||||
|
||||
$uniqid = html_writer::random_id();
|
||||
$startjs = " M.util.js_pending('" . $uniqid . "');";
|
||||
$endjs = " M.util.js_complete('" . $uniqid . "');";
|
||||
|
||||
if ($module) {
|
||||
$this->js_module($module);
|
||||
$modulename = $module['name'];
|
||||
$jscode = "Y.use('$modulename', function(Y) { $jscode });";
|
||||
$jscode = "$startjs Y.use('$modulename', function(Y) { $jscode $endjs });";
|
||||
}
|
||||
|
||||
if ($ondomready) {
|
||||
$jscode = "Y.on('domready', function() { $jscode });";
|
||||
$jscode = "$startjs Y.on('domready', function() { $jscode $endjs });";
|
||||
}
|
||||
|
||||
$this->jsinitcode[] = $jscode;
|
||||
@ -1216,7 +1220,7 @@ class page_requirements_manager {
|
||||
$output .= js_writer::function_call($data[0], $data[1], $data[2]);
|
||||
}
|
||||
if (!empty($ondomready)) {
|
||||
$output = " Y.on('domready', function() {\n$output\n });";
|
||||
$output = " Y.on('domready', function() {\n$output\n});";
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
@ -1453,6 +1457,8 @@ class page_requirements_manager {
|
||||
// Add other requested modules.
|
||||
$output = $this->get_extra_modules_code();
|
||||
|
||||
$this->js_init_code('M.util.js_complete("init");', true);
|
||||
|
||||
// All the other linked scripts - there should be as few as possible.
|
||||
if ($this->jsincludes['footer']) {
|
||||
foreach ($this->jsincludes['footer'] as $url) {
|
||||
|
@ -68,6 +68,7 @@ class behat_deprecated extends behat_base {
|
||||
// Looking for the element DOM node inside the specified row.
|
||||
list($selector, $locator) = $this->transform_selector($selectortype, $element);
|
||||
$elementnode = $this->find($selector, $locator, false, $rownode);
|
||||
$this->ensure_element_is_visible($elementnode);
|
||||
$elementnode->click();
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,9 @@ class behat_forms extends behat_base {
|
||||
*/
|
||||
public function i_fill_the_moodle_form_with(TableNode $data) {
|
||||
|
||||
// We ensure that all the editors are loaded and we can interact with them.
|
||||
$this->ensure_editors_are_loaded();
|
||||
|
||||
// Expand all fields in case we have.
|
||||
$this->expand_all_fields();
|
||||
|
||||
@ -171,31 +174,11 @@ class behat_forms extends behat_base {
|
||||
public function select_option($option, $select) {
|
||||
|
||||
$selectnode = $this->find_field($select);
|
||||
$selectnode->selectOption($option);
|
||||
|
||||
// Adding a click as Selenium requires it to fire some JS events.
|
||||
if ($this->running_javascript()) {
|
||||
|
||||
// In some browsers the selectOption actions can perform a page reload
|
||||
// so we need to ensure the element is still available to continue interacting
|
||||
// with it. We don't wait here.
|
||||
if (!$this->getSession()->getDriver()->find($selectnode->getXpath())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Single select needs an extra click in the option.
|
||||
if (!$selectnode->hasAttribute('multiple')) {
|
||||
|
||||
// Avoid quotes problems.
|
||||
$option = $this->getSession()->getSelectorsHandler()->xpathLiteral($option);
|
||||
$xpath = "//option[(./@value=$option or normalize-space(.)=$option)]";
|
||||
$optionnode = $this->find('xpath', $xpath, false, $selectnode);
|
||||
$optionnode->click();
|
||||
} else {
|
||||
// Multiple ones needs the click in the select.
|
||||
$selectnode->click();
|
||||
}
|
||||
}
|
||||
// We delegate to behat_form_field class, it will
|
||||
// guess the type properly as it is a select tag.
|
||||
$selectformfield = behat_field_manager::get_form_field($selectnode, $this->getSession());
|
||||
$selectformfield->set_value($option);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -225,6 +208,8 @@ class behat_forms extends behat_base {
|
||||
*/
|
||||
public function check_option($option) {
|
||||
|
||||
// We don't delegate to behat_form_checkbox as the
|
||||
// step is explicitly saying I check.
|
||||
$checkboxnode = $this->find_field($option);
|
||||
$checkboxnode->check();
|
||||
}
|
||||
@ -238,6 +223,8 @@ class behat_forms extends behat_base {
|
||||
*/
|
||||
public function uncheck_option($option) {
|
||||
|
||||
// We don't delegate to behat_form_checkbox as the
|
||||
// step is explicitly saying I uncheck.
|
||||
$checkboxnode = $this->find_field($option);
|
||||
$checkboxnode->uncheck();
|
||||
}
|
||||
|
@ -124,7 +124,20 @@ class behat_general extends behat_base {
|
||||
* @param string $iframename
|
||||
*/
|
||||
public function switch_to_iframe($iframename) {
|
||||
$this->getSession()->switchToIFrame($iframename);
|
||||
|
||||
// We spin to give time to the iframe to be loaded.
|
||||
// Using extended timeout as we don't know about which
|
||||
// kind of iframe will be loaded.
|
||||
$this->spin(
|
||||
function($context, $iframename) {
|
||||
$context->getSession()->switchToIFrame($iframename);
|
||||
|
||||
// If no exception we are done.
|
||||
return true;
|
||||
},
|
||||
$iframename,
|
||||
self::EXTENDED_TIMEOUT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -173,6 +186,7 @@ class behat_general extends behat_base {
|
||||
public function click_link($link) {
|
||||
|
||||
$linknode = $this->find_link($link);
|
||||
$this->ensure_node_is_visible($linknode);
|
||||
$linknode->click();
|
||||
}
|
||||
|
||||
@ -202,7 +216,56 @@ class behat_general extends behat_base {
|
||||
throw new DriverException('Waits are disabled in scenarios without Javascript support');
|
||||
}
|
||||
|
||||
$this->getSession()->wait(self::TIMEOUT, '(document.readyState === "complete")');
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the editors are all completely loaded.
|
||||
*
|
||||
* @Given /^I wait until the editors are loaded$/
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function wait_until_editors_are_loaded() {
|
||||
|
||||
if (!$this->running_javascript()) {
|
||||
throw new DriverException('Editors are not loaded when running without Javascript support');
|
||||
}
|
||||
|
||||
$this->ensure_editors_are_loaded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the provided element selector exists in the DOM
|
||||
*
|
||||
* Using the protected method as this method will be usually
|
||||
* called by other methods which are not returning a set of
|
||||
* steps and performs the actions directly, so it would not
|
||||
* be executed if it returns another step.
|
||||
|
||||
* @Given /^I wait until "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" exists$/
|
||||
* @param string $element
|
||||
* @param string $selector
|
||||
* @return void
|
||||
*/
|
||||
public function wait_until_exists($element, $selectortype) {
|
||||
$this->ensure_element_exists($element, $selectortype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the provided element does not exist in the DOM
|
||||
*
|
||||
* Using the protected method as this method will be usually
|
||||
* called by other methods which are not returning a set of
|
||||
* steps and performs the actions directly, so it would not
|
||||
* be executed if it returns another step.
|
||||
|
||||
* @Given /^I wait until "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" does not exist$/
|
||||
* @param string $element
|
||||
* @param string $selector
|
||||
* @return void
|
||||
*/
|
||||
public function wait_until_does_not_exists($element, $selectortype) {
|
||||
$this->ensure_element_does_not_exist($element, $selectortype);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -230,6 +293,7 @@ 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->ensure_node_is_visible($node);
|
||||
$node->click();
|
||||
}
|
||||
|
||||
@ -245,6 +309,7 @@ class behat_general extends behat_base {
|
||||
public function i_click_on_in_the($element, $selectortype, $nodeelement, $nodeselectortype) {
|
||||
|
||||
$node = $this->get_node_in_container($selectortype, $element, $nodeselectortype, $nodeelement);
|
||||
$this->ensure_node_is_visible($node);
|
||||
$node->click();
|
||||
}
|
||||
|
||||
@ -298,6 +363,9 @@ class behat_general extends behat_base {
|
||||
/**
|
||||
* Checks, that the specified element is not visible. Only available in tests using Javascript.
|
||||
*
|
||||
* As a "not" method, it's performance is not specially good as we should ensure that the element
|
||||
* have time to appear.
|
||||
*
|
||||
* @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>(?:[^"]|\\")*)" should not be visible$/
|
||||
* @throws ElementNotFoundException
|
||||
* @throws ExpectationException
|
||||
@ -381,24 +449,35 @@ class behat_general extends behat_base {
|
||||
$xpath = "/descendant-or-self::*[contains(., $xpathliteral)]" .
|
||||
"[count(descendant::*[contains(., $xpathliteral)]) = 0]";
|
||||
|
||||
// Wait until it finds the text, otherwise custom exception.
|
||||
try {
|
||||
$nodes = $this->find_all('xpath', $xpath);
|
||||
|
||||
// We also check for the element visibility when running JS tests.
|
||||
if ($this->running_javascript()) {
|
||||
foreach ($nodes as $node) {
|
||||
if ($node->isVisible()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ExpectationException("'{$text}' text was found but was not visible", $this->getSession());
|
||||
}
|
||||
|
||||
} catch (ElementNotFoundException $e) {
|
||||
throw new ExpectationException('"' . $text . '" text was not found in the page', $this->getSession());
|
||||
}
|
||||
|
||||
// If we are not running javascript we have enough with the
|
||||
// element existing as we can't check if it is visible.
|
||||
if (!$this->running_javascript()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We spin as we don't have enough checking that the element is there, we
|
||||
// should also ensure that the element is visible.
|
||||
$this->spin(
|
||||
function($context, $args) {
|
||||
|
||||
foreach ($args['nodes'] as $node) {
|
||||
if ($node->isVisible()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If non of the nodes is visible we loop again.
|
||||
throw new ExpectationException('"' . $args['text'] . '" text was found but was not visible', $context->getSession());
|
||||
},
|
||||
array('nodes' => $nodes, 'text' => $text)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -410,16 +489,43 @@ class behat_general extends behat_base {
|
||||
*/
|
||||
public function assert_page_not_contains_text($text) {
|
||||
|
||||
// Delegating the process to assert_page_contains_text.
|
||||
// Looking for all the matching nodes without any other descendant matching the
|
||||
// same xpath (we are using contains(., ....).
|
||||
$xpathliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text);
|
||||
$xpath = "/descendant-or-self::*[contains(., $xpathliteral)]" .
|
||||
"[count(descendant::*[contains(., $xpathliteral)]) = 0]";
|
||||
|
||||
// We should wait a while to ensure that the page is not still loading elements.
|
||||
// Giving preference to the reliability of the results rather than to the performance.
|
||||
try {
|
||||
$this->assert_page_contains_text($text);
|
||||
} catch (ExpectationException $e) {
|
||||
// It should not appear, so this is good.
|
||||
$nodes = $this->find_all('xpath', $xpath);
|
||||
} catch (ElementNotFoundException $e) {
|
||||
// All ok.
|
||||
return;
|
||||
}
|
||||
|
||||
// If the page contains the text this is failing.
|
||||
throw new ExpectationException('"' . $text . '" text was found in the page', $this->getSession());
|
||||
// If we are not running javascript we have enough with the
|
||||
// element existing as we can't check if it is hidden.
|
||||
if (!$this->running_javascript()) {
|
||||
throw new ExpectationException('"' . $text . '" text was found in the page', $this->getSession());
|
||||
}
|
||||
|
||||
// If the element is there we should be sure that it is not visible.
|
||||
$this->spin(
|
||||
function($context, $args) {
|
||||
|
||||
foreach ($args['nodes'] as $node) {
|
||||
if ($node->isVisible()) {
|
||||
throw new ExpectationException('"' . $args['text'] . '" text was found in the page', $context->getSession());
|
||||
}
|
||||
}
|
||||
|
||||
// If non of the found nodes is visible we consider that the text is not visible.
|
||||
return true;
|
||||
},
|
||||
array('nodes' => $nodes, 'text' => $text)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -446,22 +552,30 @@ class behat_general extends behat_base {
|
||||
// Wait until it finds the text inside the container, otherwise custom exception.
|
||||
try {
|
||||
$nodes = $this->find_all('xpath', $xpath, false, $container);
|
||||
} catch (ElementNotFoundException $e) {
|
||||
throw new ExpectationException('"' . $text . '" text was not found in the "' . $element . '" element', $this->getSession());
|
||||
}
|
||||
|
||||
// We also check for the element visibility when running JS tests.
|
||||
if ($this->running_javascript()) {
|
||||
foreach ($nodes as $node) {
|
||||
// If we are not running javascript we have enough with the
|
||||
// element existing as we can't check if it is visible.
|
||||
if (!$this->running_javascript()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We also check the element visibility when running JS tests.
|
||||
$this->spin(
|
||||
function($context, $args) {
|
||||
|
||||
foreach ($args['nodes'] as $node) {
|
||||
if ($node->isVisible()) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ExpectationException("'{$text}' text was found in the {$element} element but was not visible", $this->getSession());
|
||||
}
|
||||
|
||||
} catch (ElementNotFoundException $e) {
|
||||
throw new ExpectationException('"' . $text . '" text was not found in the ' . $element . ' element', $this->getSession());
|
||||
}
|
||||
|
||||
throw new ExpectationException('"' . $args['text'] . '" text was found in the "' . $args['element'] . '" element but was not visible', $context->getSession());
|
||||
},
|
||||
array('nodes' => $nodes, 'text' => $text, 'element' => $element)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -476,18 +590,45 @@ class behat_general extends behat_base {
|
||||
*/
|
||||
public function assert_element_not_contains_text($text, $element, $selectortype) {
|
||||
|
||||
// Delegating the process to assert_element_contains_text.
|
||||
// Getting the container where the text should be found.
|
||||
$container = $this->get_selected_node($selectortype, $element);
|
||||
|
||||
// Looking for all the matching nodes without any other descendant matching the
|
||||
// same xpath (we are using contains(., ....).
|
||||
$xpathliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text);
|
||||
$xpath = "/descendant-or-self::*[contains(., $xpathliteral)]" .
|
||||
"[count(descendant::*[contains(., $xpathliteral)]) = 0]";
|
||||
|
||||
// We should wait a while to ensure that the page is not still loading elements.
|
||||
// Giving preference to the reliability of the results rather than to the performance.
|
||||
try {
|
||||
$this->assert_element_contains_text($text, $element, $selectortype);
|
||||
} catch (ExpectationException $e) {
|
||||
// It should not appear, so this is good.
|
||||
// We only catch ExpectationException as ElementNotFoundException
|
||||
// will be thrown if the container does not exist.
|
||||
$nodes = $this->find_all('xpath', $xpath, false, $container);
|
||||
} catch (ElementNotFoundException $e) {
|
||||
// All ok.
|
||||
return;
|
||||
}
|
||||
|
||||
// If the element contains the text this is failing.
|
||||
throw new ExpectationException('"' . $text . '" text was found in the ' . $element . ' element', $this->getSession());
|
||||
// If we are not running javascript we have enough with the
|
||||
// element not being found as we can't check if it is visible.
|
||||
if (!$this->running_javascript()) {
|
||||
throw new ExpectationException('"' . $text . '" text was found in the "' . $element . '" element', $this->getSession());
|
||||
}
|
||||
|
||||
// We need to ensure all the found nodes are hidden.
|
||||
$this->spin(
|
||||
function($context, $args) {
|
||||
|
||||
foreach ($args['nodes'] as $node) {
|
||||
if ($node->isVisible()) {
|
||||
throw new ExpectationException('"' . $args['text'] . '" text was found in the "' . $args['element'] . '" element', $context->getSession());
|
||||
}
|
||||
}
|
||||
|
||||
// If all the found nodes are hidden we are happy.
|
||||
return true;
|
||||
},
|
||||
array('nodes' => $nodes, 'text' => $text, 'element' => $element)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -222,42 +222,68 @@ class behat_hooks extends behat_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that all DOM is ready.
|
||||
* Wait for JS to complete before beginning interacting with the DOM.
|
||||
*
|
||||
* Executed only when running against a real browser.
|
||||
*
|
||||
* @BeforeStep @javascript
|
||||
*/
|
||||
public function before_step_javascript($event) {
|
||||
$this->wait_for_pending_js();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for JS to complete after finishing the step.
|
||||
*
|
||||
* With this we ensure that there are not AJAX calls
|
||||
* still in progress.
|
||||
*
|
||||
* Executed only when running against a real browser.
|
||||
*
|
||||
* @AfterStep @javascript
|
||||
*/
|
||||
public function after_step_javascript($event) {
|
||||
$this->wait_for_pending_js();
|
||||
}
|
||||
|
||||
// If it doesn't have definition or it fails there is no need to check it.
|
||||
if ($event->getResult() != StepEvent::PASSED ||
|
||||
!$event->hasDefinition()) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Waits for all the JS to be loaded.
|
||||
*
|
||||
* @throws NoSuchWindow
|
||||
* @throws UnknownError
|
||||
* @return bool True or false depending whether all the JS is loaded or not.
|
||||
*/
|
||||
protected function wait_for_pending_js() {
|
||||
|
||||
// Wait until the page is ready.
|
||||
// We are already checking that we use a JS browser, this could
|
||||
// change in case we use another JS driver.
|
||||
try {
|
||||
|
||||
// Safari and Internet Explorer requires time between steps,
|
||||
// otherwise Selenium tries to click in the previous page's DOM.
|
||||
if ($this->getSession()->getDriver()->getBrowserName() == 'safari' ||
|
||||
$this->getSession()->getDriver()->getBrowserName() == 'internet explorer') {
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, false);
|
||||
|
||||
} else {
|
||||
// With other browsers we just wait for the DOM ready.
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, '(document.readyState === "complete")');
|
||||
// We don't use behat_base::spin() here as we don't want to end up with an exception
|
||||
// if the page & JSs don't finish loading properly.
|
||||
for ($i = 0; $i < self::EXTENDED_TIMEOUT * 10; $i++) {
|
||||
$pending = '';
|
||||
try {
|
||||
$jscode = 'return ' . self::PAGE_READY_JS . ' ? "" : M.util.pending_js.join(":");';
|
||||
$pending = $this->getSession()->evaluateScript($jscode);
|
||||
} catch (NoSuchWindow $nsw) {
|
||||
// We catch an exception here, in case we just closed the window we were interacting with.
|
||||
// No javascript is running if there is no window right?
|
||||
$pending = '';
|
||||
} catch (UnknownError $e) {
|
||||
// Same exception as before, but some combinations of browser + OS reports it as an unknown error
|
||||
// exception.
|
||||
$pending = '';
|
||||
}
|
||||
|
||||
} catch (NoSuchWindow $e) {
|
||||
// If we were interacting with a popup window it will not exists after closing it.
|
||||
} catch (UnknownError $e) {
|
||||
// Custom exception to provide more feedback about possible solutions.
|
||||
$this->throw_unknown_exception($e);
|
||||
// If there are no pending JS we stop waiting.
|
||||
if ($pending === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0.1 seconds.
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
// Timeout waiting for JS to complete.
|
||||
// TODO MDL-43173 We should fail the scenarios if JS loading times out.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,6 +76,7 @@ class behat_navigation extends behat_base {
|
||||
|
||||
$exception = new ExpectationException('The "' . $nodetext . '" node can not be expanded', $this->getSession());
|
||||
$node = $this->find('xpath', $xpath, $exception);
|
||||
$this->ensure_node_is_visible($node);
|
||||
$node->click();
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,16 @@ class behat_permissions extends behat_base {
|
||||
try {
|
||||
$advancedtoggle = $this->find_button(get_string('showadvanced', 'form'));
|
||||
if ($advancedtoggle) {
|
||||
$this->getSession()->getPage()->pressButton(get_string('showadvanced', 'form'));
|
||||
|
||||
// As we are interacting with a moodle form we wait for the editor to be ready
|
||||
// otherwise we may have problems when setting values on it or clicking on elements
|
||||
// as the position of the elements will change once the editor is loaded.
|
||||
$this->ensure_editors_are_loaded();
|
||||
|
||||
$advancedtoggle->click();
|
||||
|
||||
// Wait for the page to load.
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// We already are in advanced mode.
|
||||
|
@ -38,10 +38,10 @@ Feature: In an assignment, students can upload files for assessment
|
||||
And I should see "Not graded"
|
||||
And I press "Edit submission"
|
||||
And I upload "lib/tests/fixtures/upload_users.csv" file to "File submissions" filemanager
|
||||
And ".ffilemanager .fm-maxfiles .fp-btn-add" "css_element" should exists
|
||||
And ".ffilemanager .fm-maxfiles .fp-btn-add" "css_element" should not be visible
|
||||
And I press "Save changes"
|
||||
And I should see "Submitted for grading"
|
||||
And I should see "empty.txt"
|
||||
And I should see "upload_users.csv"
|
||||
And I press "Edit submission"
|
||||
And ".ffilemanager .fm-maxfiles .fp-btn-add" "css_element" should exists
|
||||
And ".ffilemanager .fm-maxfiles .fp-btn-add" "css_element" should not be visible
|
||||
|
@ -25,5 +25,4 @@ Feature: Add forum activities and discussions
|
||||
When I add a new discussion to "Test forum name" forum with:
|
||||
| Subject | Forum post 1 |
|
||||
| Message | This is the body |
|
||||
And I wait "6" seconds
|
||||
Then I should see "Test forum name"
|
||||
|
@ -41,7 +41,6 @@ Feature: Teachers can edit or delete any forum post
|
||||
And I follow "Teacher post subject"
|
||||
And I click on "Delete" "link" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' forumpost ')][contains(., 'Student post subject')]" "xpath_element"
|
||||
And I press "Continue"
|
||||
And I wait "4" seconds
|
||||
Then I should not see "Student post subject"
|
||||
And I should not see "Student post message"
|
||||
|
||||
@ -56,7 +55,7 @@ Feature: Teachers can edit or delete any forum post
|
||||
And I fill the moodle form with:
|
||||
| Subject | Edited student subject |
|
||||
And I press "Save changes"
|
||||
And I wait "4" seconds
|
||||
And I wait to be redirected
|
||||
Then I should see "Edited student subject"
|
||||
And I should see "Edited by Teacher 1 - original submission"
|
||||
|
||||
|
@ -30,7 +30,6 @@ Feature: A teacher can set one of 3 possible options for tracking read forum pos
|
||||
And I add a new discussion to "Test forum name" forum with:
|
||||
| Subject | Test post subject |
|
||||
| Message | Test post message |
|
||||
And I wait "6" seconds
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
@ -48,19 +47,18 @@ Feature: A teacher can set one of 3 possible options for tracking read forum pos
|
||||
And I add a new discussion to "Test forum name" forum with:
|
||||
| Subject | Test post subject |
|
||||
| Message | Test post message |
|
||||
And I wait "6" seconds
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
Then I should see "1 unread post"
|
||||
And I follow "Test forum name"
|
||||
And I follow "Don't track unread posts"
|
||||
And I wait "4" seconds
|
||||
And I wait to be redirected
|
||||
And I follow "Course 1"
|
||||
And I should not see "1 unread post"
|
||||
And I follow "Test forum name"
|
||||
And I follow "Track unread posts"
|
||||
And I wait "4" seconds
|
||||
And I wait to be redirected
|
||||
And I follow "1"
|
||||
And I follow "Course 1"
|
||||
And I should not see "1 unread post"
|
||||
@ -75,7 +73,6 @@ Feature: A teacher can set one of 3 possible options for tracking read forum pos
|
||||
And I add a new discussion to "Test forum name" forum with:
|
||||
| Subject | Test post subject |
|
||||
| Message | Test post message |
|
||||
And I wait "6" seconds
|
||||
And I log out
|
||||
When I log in as "student2"
|
||||
And I follow "Course 1"
|
||||
@ -85,7 +82,7 @@ Feature: A teacher can set one of 3 possible options for tracking read forum pos
|
||||
|
||||
@javascript
|
||||
Scenario: Tracking forum posts forced with user tracking on
|
||||
And I set the following administration settings values:
|
||||
Given I set the following administration settings values:
|
||||
| Allow forced read tracking | 1 |
|
||||
And I follow "Home"
|
||||
And I follow "Course 1"
|
||||
@ -97,7 +94,6 @@ Feature: A teacher can set one of 3 possible options for tracking read forum pos
|
||||
And I add a new discussion to "Test forum name" forum with:
|
||||
| Subject | Test post subject |
|
||||
| Message | Test post message |
|
||||
And I wait "6" seconds
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
@ -110,7 +106,7 @@ Feature: A teacher can set one of 3 possible options for tracking read forum pos
|
||||
|
||||
@javascript
|
||||
Scenario: Tracking forum posts forced with user tracking off
|
||||
And I set the following administration settings values:
|
||||
Given I set the following administration settings values:
|
||||
| Allow forced read tracking | 1 |
|
||||
And I follow "Home"
|
||||
And I follow "Course 1"
|
||||
@ -122,7 +118,6 @@ Feature: A teacher can set one of 3 possible options for tracking read forum pos
|
||||
And I add a new discussion to "Test forum name" forum with:
|
||||
| Subject | Test post subject |
|
||||
| Message | Test post message |
|
||||
And I wait "6" seconds
|
||||
And I log out
|
||||
When I log in as "student2"
|
||||
And I follow "Course 1"
|
||||
@ -135,7 +130,7 @@ Feature: A teacher can set one of 3 possible options for tracking read forum pos
|
||||
|
||||
@javascript
|
||||
Scenario: Tracking forum posts forced (with force disabled) with user tracking on
|
||||
And I set the following administration settings values:
|
||||
Given I set the following administration settings values:
|
||||
| Allow forced read tracking | 1 |
|
||||
And I follow "Home"
|
||||
And I follow "Course 1"
|
||||
@ -147,7 +142,6 @@ Feature: A teacher can set one of 3 possible options for tracking read forum pos
|
||||
And I add a new discussion to "Test forum name" forum with:
|
||||
| Subject | Test post subject |
|
||||
| Message | Test post message |
|
||||
And I wait "6" seconds
|
||||
And I set the following administration settings values:
|
||||
| Allow forced read tracking | 0 |
|
||||
And I log out
|
||||
@ -156,19 +150,19 @@ Feature: A teacher can set one of 3 possible options for tracking read forum pos
|
||||
Then I should see "1 unread post"
|
||||
And I follow "Test forum name"
|
||||
And I follow "Don't track unread posts"
|
||||
And I wait "4" seconds
|
||||
And I wait to be redirected
|
||||
And I follow "Course 1"
|
||||
And I should not see "1 unread post"
|
||||
And I follow "Test forum name"
|
||||
And I follow "Track unread posts"
|
||||
And I wait "4" seconds
|
||||
And I wait to be redirected
|
||||
And I follow "1"
|
||||
And I follow "Course 1"
|
||||
And I should not see "1 unread post"
|
||||
|
||||
@javascript
|
||||
Scenario: Tracking forum posts forced (with force disabled) with user tracking off
|
||||
And I set the following administration settings values:
|
||||
Given I set the following administration settings values:
|
||||
| Allow forced read tracking | 1 |
|
||||
And I follow "Home"
|
||||
And I follow "Course 1"
|
||||
@ -180,7 +174,6 @@ Feature: A teacher can set one of 3 possible options for tracking read forum pos
|
||||
And I add a new discussion to "Test forum name" forum with:
|
||||
| Subject | Test post subject |
|
||||
| Message | Test post message |
|
||||
And I wait "6" seconds
|
||||
And I set the following administration settings values:
|
||||
| Allow forced read tracking | 0 |
|
||||
And I log out
|
||||
|
@ -1,4 +1,4 @@
|
||||
@mod @mod_scorm
|
||||
@mod @mod_scorm @_only_local @_switch_frame
|
||||
Feature: Add scorm activity
|
||||
In order to let students access a scorm package
|
||||
As a teacher
|
||||
@ -21,10 +21,10 @@ Feature: Add scorm activity
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add a "SCORM package" to section "1"
|
||||
And I upload "mod/scorm/tests/packages/singlescobasic.zip" file to "Package file" filemanager
|
||||
And I fill the moodle form with:
|
||||
| Name | Awesome SCORM package |
|
||||
| Description | Description |
|
||||
And I upload "mod/scorm/tests/packages/singlescobasic.zip" file to "Package file" filemanager
|
||||
And I click on "Save and display" "button"
|
||||
Then I should see "Awesome SCORM package"
|
||||
And I should see "Normal"
|
||||
@ -35,7 +35,6 @@ Feature: Add scorm activity
|
||||
And I follow "Awesome SCORM package"
|
||||
And I should see "Normal"
|
||||
And I press "Enter"
|
||||
And I wait "5" seconds
|
||||
And I switch to "scorm_object" iframe
|
||||
And I switch to "contentFrame" iframe
|
||||
And I should see "Play of the game"
|
||||
|
@ -38,21 +38,23 @@ M.mod_wiki.init = function(Y, args) {
|
||||
});
|
||||
new WikiHelper(args);
|
||||
};
|
||||
M.mod_wiki.renew_lock = function(Y, args) {
|
||||
function renewLock() {
|
||||
var args = {};
|
||||
args['sesskey'] = M.cfg.sesskey;
|
||||
args['pageid'] = wiki.pageid;
|
||||
if (wiki.section) {
|
||||
args['section'] = wiki.section;
|
||||
}
|
||||
var callback = {};
|
||||
Y.use('yui2-connection', function(Y) {
|
||||
Y.YUI2.util.Connect.asyncRequest('GET', 'lock.php?' + build_querystring(args), callback);
|
||||
});
|
||||
M.mod_wiki.renew_lock = function() {
|
||||
var args = {
|
||||
sesskey: M.cfg.sesskey,
|
||||
pageid: wiki.pageid
|
||||
};
|
||||
if (wiki.section) {
|
||||
args.section = wiki.section;
|
||||
}
|
||||
setInterval(renewLock, wiki.renew_lock_timeout * 1000);
|
||||
}
|
||||
YUI().use('io', function(Y) {
|
||||
function renewLock() {
|
||||
Y.io('lock.php?' + build_querystring(args), {
|
||||
method: 'POST'
|
||||
});
|
||||
}
|
||||
setInterval(renewLock, wiki.renew_lock_timeout * 1000);
|
||||
});
|
||||
};
|
||||
M.mod_wiki.history = function(Y, args) {
|
||||
var compare = false;
|
||||
var comparewith = false;
|
||||
|
@ -32,7 +32,8 @@ Feature: Edited wiki pages may be previewed before saving
|
||||
And I fill the moodle form with:
|
||||
| HTML format | Student page contents to be previewed |
|
||||
And I press "Preview"
|
||||
Then I should see "This is a preview. Changes have not been saved yet"
|
||||
Then I expand all fieldsets
|
||||
And I should see "This is a preview. Changes have not been saved yet"
|
||||
And I should see "Student page contents to be previewed"
|
||||
And I press "Save"
|
||||
And I should see "Student page contents to be previewed"
|
||||
|
@ -60,7 +60,7 @@ class behat_question extends behat_base {
|
||||
new Given('I follow "' . get_string('questionbank', 'question') . '"'),
|
||||
new Given('I press "' . get_string('createnewquestion', 'question') . '"'),
|
||||
new Given('I click on "' . $this->escape($questiontypexpath) . '" "xpath_element"'),
|
||||
new Given('I click on "Next" "button" in the "#qtypechoicecontainer" "css_element"'),
|
||||
new Given('I click on "#chooseqtype_submit" "css_element"'),
|
||||
new Given('I fill the moodle form with:', $questiondata),
|
||||
new Given('I press "' . get_string('savechanges') . '"')
|
||||
);
|
||||
|
@ -4,7 +4,7 @@ Feature: A teacher can edit questions in the question bank
|
||||
As a teacher
|
||||
I need to edit questions
|
||||
|
||||
@javascript
|
||||
@javascript @_switch_window
|
||||
Scenario: Edit a previously created question
|
||||
Given the following "users" exists:
|
||||
| username | firstname | lastname | email |
|
||||
|
@ -1,4 +1,4 @@
|
||||
@core @core_question
|
||||
@core @core_question @_switch_window
|
||||
Feature: A teacher can preview questions in the question bank
|
||||
In order to ensure the questions are properly created
|
||||
As a teacher
|
||||
|
@ -68,12 +68,6 @@ class behat_filepicker extends behat_files {
|
||||
$dialognode = $this->find('css', '.moodle-dialogue-focused');
|
||||
$buttonnode = $this->find('css', '.fp-dlg-butcreate', $exception, $dialognode);
|
||||
$buttonnode->click();
|
||||
|
||||
// Wait until the process finished and modal windows are hidden.
|
||||
$this->wait_until_return_to_form();
|
||||
|
||||
// Wait until the current folder contents are updated
|
||||
$this->wait_until_contents_are_updated($fieldnode);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,9 +87,6 @@ class behat_filepicker extends behat_files {
|
||||
$this->getSession()
|
||||
);
|
||||
|
||||
// Just in case there is any contents refresh in progress.
|
||||
$this->wait_until_contents_are_updated($fieldnode);
|
||||
|
||||
$folderliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($foldername);
|
||||
|
||||
// We look both in the pathbar and in the contents.
|
||||
@ -124,9 +115,6 @@ class behat_filepicker extends behat_files {
|
||||
|
||||
// It should be a NodeElement, otherwise an exception would have been thrown.
|
||||
$folder->click();
|
||||
|
||||
// Wait until the current folder contents are updated
|
||||
$this->wait_until_contents_are_updated($fieldnode);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,13 +133,6 @@ class behat_filepicker extends behat_files {
|
||||
// Execute the action.
|
||||
$exception = new ExpectationException($filename.' element can not be unzipped', $this->getSession());
|
||||
$this->perform_on_element('unzip', $exception);
|
||||
|
||||
// Wait until the process finished and modal windows are hidden.
|
||||
$this->wait_until_return_to_form();
|
||||
|
||||
// Wait until the current folder contents are updated
|
||||
$containernode = $this->get_filepicker_node($filemanagerelement);
|
||||
$this->wait_until_contents_are_updated($containernode);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -170,13 +151,6 @@ class behat_filepicker extends behat_files {
|
||||
// Execute the action.
|
||||
$exception = new ExpectationException($foldername.' element can not be zipped', $this->getSession());
|
||||
$this->perform_on_element('zip', $exception);
|
||||
|
||||
// Wait until the process finished and modal windows are hidden.
|
||||
$this->wait_until_return_to_form();
|
||||
|
||||
// Wait until the current folder contents are updated
|
||||
$containernode = $this->get_filepicker_node($filemanagerelement);
|
||||
$this->wait_until_contents_are_updated($containernode);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -200,13 +174,6 @@ class behat_filepicker extends behat_files {
|
||||
// Using xpath + click instead of pressButton as 'Ok' it is a common string.
|
||||
$okbutton = $this->find('css', 'div.fp-dlg button.fp-dlg-butconfirm');
|
||||
$okbutton->click();
|
||||
|
||||
// Wait until the process finished and modal windows are hidden.
|
||||
$this->wait_until_return_to_form();
|
||||
|
||||
// Wait until file manager contents are updated.
|
||||
$containernode = $this->get_filepicker_node($filemanagerelement);
|
||||
$this->wait_until_contents_are_updated($containernode);
|
||||
}
|
||||
|
||||
|
||||
@ -220,7 +187,6 @@ class behat_filepicker extends behat_files {
|
||||
*/
|
||||
public function i_should_see_elements_in_filemanager($elementscount, $filemanagerelement) {
|
||||
$filemanagernode = $this->get_filepicker_node($filemanagerelement);
|
||||
$this->wait_until_contents_are_updated($filemanagernode);
|
||||
$elements = $this->find_all('css', '.fp-content .fp-file', false, $filemanagernode);
|
||||
if (count($elements) != $elementscount) {
|
||||
throw new ExpectationException('Found '.count($elements).' elements in filemanager instead of expected '.$elementscount);
|
||||
@ -298,9 +264,6 @@ class behat_filepicker extends behat_files {
|
||||
$overwriteaction = false) {
|
||||
$filemanagernode = $this->get_filepicker_node($filemanagerelement);
|
||||
|
||||
// Wait until file manager is completely loaded.
|
||||
$this->wait_until_contents_are_updated($filemanagernode);
|
||||
|
||||
// Opening the select repository window and selecting the upload repository.
|
||||
$this->open_add_file_window($filemanagernode, $repository);
|
||||
|
||||
@ -323,16 +286,18 @@ class behat_filepicker extends behat_files {
|
||||
|
||||
$this->find_button(get_string('getfile', 'repository'))->click();
|
||||
|
||||
// We wait for all the JS to finish as it is performing an action.
|
||||
$this->getSession()->wait(self::TIMEOUT, self::PAGE_READY_JS);
|
||||
|
||||
if ($overwriteaction !== false) {
|
||||
$this->getSession()->wait(1 * 1000, false);
|
||||
$this->find_button($overwriteaction)->click();
|
||||
$overwritebutton = $this->find_button($overwriteaction);
|
||||
$this->ensure_node_is_visible($overwritebutton);
|
||||
$overwritebutton->click();
|
||||
|
||||
// We wait for all the JS to finish.
|
||||
$this->getSession()->wait(self::TIMEOUT, self::PAGE_READY_JS);
|
||||
}
|
||||
|
||||
// Ensure the file has been uploaded and all ajax processes finished.
|
||||
$this->wait_until_return_to_form();
|
||||
|
||||
// Wait until file manager contents are updated.
|
||||
$this->wait_until_contents_are_updated($filemanagernode);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -109,14 +109,10 @@ class behat_repository_upload extends behat_files {
|
||||
|
||||
$filemanagernode = $this->get_filepicker_node($filemanagerelement);
|
||||
|
||||
// Wait until file manager is completely loaded.
|
||||
$this->wait_until_contents_are_updated($filemanagernode);
|
||||
|
||||
// Opening the select repository window and selecting the upload repository.
|
||||
$this->open_add_file_window($filemanagernode, get_string('pluginname', 'repository_upload'));
|
||||
|
||||
// Ensure all the form is ready.
|
||||
$this->getSession()->wait(2 * 1000, false);
|
||||
$noformexception = new ExpectationException('The upload file form is not ready', $this->getSession());
|
||||
$this->find(
|
||||
'xpath',
|
||||
@ -161,16 +157,18 @@ class behat_repository_upload extends behat_files {
|
||||
$submit = $this->find_button(get_string('upload', 'repository'));
|
||||
$submit->press();
|
||||
|
||||
// We wait for all the JS to finish as it is performing an action.
|
||||
$this->getSession()->wait(self::TIMEOUT, self::PAGE_READY_JS);
|
||||
|
||||
if ($overwriteaction !== false) {
|
||||
$this->getSession()->wait(1 * 1000, false);
|
||||
$this->find_button($overwriteaction)->click();
|
||||
$overwritebutton = $this->find_button($overwriteaction);
|
||||
$this->ensure_node_is_visible($overwritebutton);
|
||||
$overwritebutton->click();
|
||||
|
||||
// We wait for all the JS to finish.
|
||||
$this->getSession()->wait(self::TIMEOUT, self::PAGE_READY_JS);
|
||||
}
|
||||
|
||||
// Ensure the file has been uploaded and all ajax processes finished.
|
||||
$this->wait_until_return_to_form();
|
||||
|
||||
// Wait until file manager contents are updated.
|
||||
$this->wait_until_contents_are_updated($filemanagernode);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user