/** * Creates the specified element. More info about available elements in http://docs.moodle.org/dev/Acceptance_testing#Fixtures. * * @Given /^the following "(?P<element_string>(?:[^"]|\\")*)" instances exist:$/ * * This step overrides behat generator step and accepts following parameters to call generator steps for multiple instances. * - instances : Number of instances to create * * It replaces #!count!# with the incremental value. * - refencecont: This is used in case you want a counter to be modulo of some value. * * It replaces #!count#! with incremental value. * * #!refcount!# will * - repeat: This will repeat the table row x times. * * Replaces #!repeatcount!3 with the repeating value. * * @throws Exception * @throws PendingException * @param string $elementname The name of the entity to add * @param TableNode $data */ public function the_following_instances_exist($elementname, TableNode $data) { $datageneratorcontext = behat_context_helper::get('behat_data_generators'); $datanodes = $this->fix_data_counter($data); foreach ($datanodes as $datanode) { generator::dot(); $datageneratorcontext->the_following_exist($elementname, $datanode); } }
/** * Upload the responses previously saved above, to a given filepicker. * * @param string $fieldlabel the lable of the file manager to upload to. * * @Given /^I upload the saved responses file to "([^"]*)" filemanager$/ */ public function i_upload_the_saved_responses_file_to_filemanager($fieldlabel) { $session = $this->getSession(); if ($this->downloadedfile === null) { throw new ExpectationException('No responses downloaded yet, so we can\'t upload them.', $session); } $uploadcontext = behat_context_helper::get('behat_repository_upload'); $uploadcontext->i_upload_file_to_filemanager($this->downloadedfile, $fieldlabel); }
/** * Logs in the user. There should exist a user with the same value as username and password. * * @Given /^I log in as "(?P<username_string>(?:[^"]|\\")*)"$/ */ public function i_log_in_as($username) { // Visit login page. $this->getSession()->visit($this->locate_path('login/index.php')); // Enter username and password. $behatforms = behat_context_helper::get('behat_forms'); $behatforms->i_set_the_field_to('Username', $this->escape($username)); $behatforms->i_set_the_field_to('Password', $this->escape($username)); // Press log in button. $behatforms->press_button(get_string('login')); }
/** * Drag the drag item with the given text to the given space. * * @param string $marker the marker to drag. The label, optionally followed by ,<instance number> (int) if relevant. * @param string $coordinates the position to drag the marker to, 'x,y'. * * @Given /^I drag "(?P<marker>[^"]*)" to "(?P<coordinates>\d+,\d+)" in the drag and drop markers question$/ */ public function i_drag_to_in_the_drag_and_drop_markers_question($marker, $coordinates) { list($marker, $item) = $this->parse_marker_name($marker); list($x, $y) = explode(',', $coordinates); // This is a bit nasty, but Behat (indeed Selenium) will only drag on // DOM node so that its centre is over the centre of anothe DOM node. // Therefore to make it drag to the specified place, we have to add // a target div. $session = $this->getSession(); $session->evaluateScript("\n (function() {\n if (document.getElementById('target-{$x}-{$y}')) {\n return;\n }\n var image = document.querySelector('.dropbackground');\n var target = document.createElement('div');\n target.setAttribute('id', 'target-{$x}-{$y}');\n var container = document.querySelector('.droparea');\n container.style.setProperty('position', 'relative');\n container.insertBefore(target, image);\n var xadjusted = {$x} + (container.offsetWidth - image.offsetWidth) / 2;\n var yadjusted = {$y} + (container.offsetHeight - image.offsetHeight) / 2;\n target.style.setProperty('position', 'absolute');\n target.style.setProperty('left', xadjusted + 'px');\n target.style.setProperty('top', yadjusted + 'px');\n target.style.setProperty('width', '1px');\n target.style.setProperty('height', '1px');\n }())"); $generalcontext = behat_context_helper::get('behat_general'); $generalcontext->i_drag_and_i_drop_it_in($this->marker_xpath($marker, $item), 'xpath_element', "#target-{$x}-{$y}", 'css_element'); }
/** * Chooses the specified option from the choice activity named as specified and save the choice. * You should be located in the activity's course page. * * @Given /^I choose options (?P<option_string>"(?:[^"]|\\")*"(?:,"(?:[^"]|\\")*")*) from "(?P<choice_activity_string>(?:[^"]|\\")*)" choice activity$/ * @param string $option * @param string $choiceactivity * @return array */ public function I_choose_options_from_activity($option, $choiceactivity) { // Get Behat general and forms contexts. $behatgeneral = behat_context_helper::get('behat_general'); $behatforms = behat_context_helper::get('behat_forms'); // Go to choice activity. $behatgeneral->click_link($this->escape($choiceactivity)); // Wait for page to be loaded. $this->wait_for_pending_js(); // Set all options. $options = explode('","', trim($option, '"')); foreach ($options as $option) { $behatforms->i_set_the_field_to($this->escape($option), '1'); } // Save choice. $behatforms->press_button(get_string('savemychoice', 'choice')); }
/** * Helper function to execute api in a given context. * * @param string $contextapi context in which api is defined. * @param array $params list of params to pass. * @throws Exception */ protected function execute($contextapi, $params = array()) { if (!is_array($params)) { $params = array($params); } // Get required context and execute the api. $contextapi = explode("::", $contextapi); $context = behat_context_helper::get($contextapi[0]); call_user_func_array(array($context, $contextapi[1]), $params); // NOTE: Wait for pending js and look for exception are not optional, as this might lead to unexpected results. // Don't make them optional for performance reasons. // Wait for pending js. $this->wait_for_pending_js(); // Look for exceptions. $this->look_for_exceptions(); }
/** * Click link in navigation tree that matches the text in parentnode/s (seperated using greater-than character if more than one) * * @Given /^I navigate to "(?P<nodetext_string>(?:[^"]|\\")*)" node in "(?P<parentnodes_string>(?:[^"]|\\")*)"$/ * * @throws ExpectationException * @param string $nodetext navigation node to click. * @param string $parentnodes comma seperated list of parent nodes. * @return void */ public function i_navigate_to_node_in($nodetext, $parentnodes) { // Site admin is different and needs special treatment. $siteadminstr = get_string('administrationsite'); // Create array of all parentnodes. $parentnodes = array_map('trim', explode('>', $parentnodes)); $countparentnode = count($parentnodes); // If JS is disabled and Site administration is not expanded we // should follow it, so all the lower-level nodes are available. if (!$this->running_javascript()) { if ($parentnodes[0] === $siteadminstr) { // We don't know if there if Site admin is already expanded so // don't wait, it is non-JS and we already waited for the DOM. $siteadminlink = $this->getSession()->getPage()->find('named_exact', array('link', "'" . $siteadminstr . "'")); if ($siteadminlink) { $siteadminlink->click(); } } } // Get top level node. $node = $this->get_top_navigation_node($parentnodes[0]); // Expand all nodes. for ($i = 0; $i < $countparentnode; $i++) { if ($i > 0) { // Sub nodes within top level node. $node = $this->get_navigation_node($parentnodes[$i], $node); } // The p node contains the aria jazz. $pnodexpath = "/p[contains(concat(' ', normalize-space(@class), ' '), ' tree_item ')]"; $pnode = $node->find('xpath', $pnodexpath); // Keep expanding all sub-parents if js enabled. if ($pnode && $this->running_javascript() && $pnode->hasAttribute('aria-expanded') && $pnode->getAttribute('aria-expanded') == "false") { $this->ensure_node_is_visible($pnode); // If node is a link then some driver click in the middle of the node, which click on link and // page gets redirected. To ensure expansion works in all cases, check if the node to expand is a // link and if yes then click on link and wait for it to navigate to next page with node expanded. $nodetoexpandliteral = behat_context_helper::escape($parentnodes[$i]); $nodetoexpandxpathlink = $pnodexpath . "/a[normalize-space(.)=" . $nodetoexpandliteral . "]"; if ($nodetoexpandlink = $node->find('xpath', $nodetoexpandxpathlink)) { $behatgeneralcontext = behat_context_helper::get('behat_general'); $nodetoexpandlink->click(); $behatgeneralcontext->wait_until_the_page_is_ready(); } else { $pnode->click(); } // Wait for node to load, if not loaded before. if ($pnode->hasAttribute('data-loaded') && $pnode->getAttribute('data-loaded') == "false") { $jscondition = '(document.evaluate("' . $pnode->getXpath() . '", document, null, ' . 'XPathResult.ANY_TYPE, null).iterateNext().getAttribute(\'data-loaded\') == "true")'; $this->getSession()->wait(self::EXTENDED_TIMEOUT * 1000, $jscondition); } } } // Finally, click on requested node under navigation. $nodetextliteral = behat_context_helper::escape($nodetext); $xpath = "/ul/li/p[contains(concat(' ', normalize-space(@class), ' '), ' tree_item ')]" . "/a[normalize-space(.)=" . $nodetextliteral . "]"; $nodetoclick = $node->find('xpath', $xpath); // Throw exception if no node found. if (!$nodetoclick) { throw new ExpectationException('Navigation node "' . $nodetext . '" not found under "' . implode($parentnodes, ' > ') . '"', $this->getSession()); } $nodetoclick->click(); }
/** * Set the response for a given input. * * @param string $identifier the text of the item to drag. E.g. '2:answer'. * @param string $value the response to give. * * @Given /^I set the input "(?P<name>[^"]*)" to "(?P<value>[^"]*)" in the STACK question$/ */ public function i_set_the_part_to_in_the_combined_question($name, $value) { $formscontext = behat_context_helper::get('behat_forms'); $formscontext->i_set_the_field_with_xpath_to($this->input_xpath($name), $value); }
/** * Exports feedback and makes sure the export file is the same as in the fixture * * @Then /^following "(?P<link_string>(?:[^"]|\\")*)" should export feedback identical to "(?P<filename_string>(?:[^"]|\\")*)"$/ * @param string $link * @param string $filename */ public function following_should_export_feedback_identical_to($link, $filename) { global $CFG; $exception = new ExpectationException('Error while downloading data from ' . $link, $this->getSession()); // It will stop spinning once file is downloaded or time out. $behatgeneralcontext = behat_context_helper::get('behat_general'); $result = $this->spin(function ($context, $args) use($behatgeneralcontext) { $link = $args['link']; return $behatgeneralcontext->download_file_from_link($link); }, array('link' => $link), self::EXTENDED_TIMEOUT, $exception); $this->compare_exports(file_get_contents($CFG->dirroot . '/' . $filename), $result); }
/** * Sets the field value. * * @param string $value * @return void */ public function set_value($value) { // Getting the filemanager label from the DOM. $fieldlabel = $this->get_field_locator(); // Getting the filepicker context and using the step definition // to upload the requested file. $uploadcontext = behat_context_helper::get('behat_repository_upload'); $uploadcontext->i_upload_file_to_filemanager($value, $fieldlabel); }
/** * Drag the drag item with the given text to the given space. * * @param string $dragitem the text of the item to drag. * @param int $placenumber the number of the place to drop into. * * @Given /^I drag "(?P<drag_item>[^"]*)" to place "(?P<place_number>\d+)" in the drag and drop onto image question$/ * */ public function i_drag_to_place_in_the_drag_and_drop_onto_image_question($dragitem, $placenumber) { $generalcontext = behat_context_helper::get('behat_general'); $generalcontext->i_drag_and_i_drop_it_in($this->drag_xpath($dragitem), 'xpath_element', $this->drop_xpath($placenumber), 'xpath_element'); }
/** * Helper function to execute api in a given context. * Backwards compatibility for pre 3.0 versions. * NOTE: Wait for pending js and look for exception should be called, * to avoid unexpected results. But they are not implemented here, so * pre 3.0 use might encounter issues. * * @param string $contextapi context in which api is defined. * @param array $params list of params to pass. * @throws Exception */ protected function own_execute($contextapi, $params = array()) { if (method_exists($this, 'execute')) { $this->execute($contextapi, $params); return; } if (!is_array($params)) { $params = array($params); } // Get required context and execute the api. $contextapi = explode("::", $contextapi); $context = behat_context_helper::get($contextapi[0]); call_user_func_array(array($context, $contextapi[1]), $params); }
/** * Drag the drag item with the given text to the given space. * * @param int $spacenumber the number of the gap to drop into. * @param string $value the text of the response to select. * * @Given /^I set space "(?P<space_number>\d+)" to "(?P<value>[^"]*)" in the select missing words question$/ */ public function i_set_space_to_in_the_select_missing_words_question($spacenumber, $value) { $formscontext = behat_context_helper::get('behat_forms'); $formscontext->i_set_the_field_with_xpath_to($this->space_xpath($spacenumber), $value); }
/** * Sends a message to the specified user from the logged user. The user full name should contain the first and last names. * * @Given /^I send "(?P<message_contents_string>(?:[^"]|\\")*)" message to "(?P<user_full_name_string>(?:[^"]|\\")*)" user \(theme_snap\)$/ * @param string $messagecontent * @param string $userfullname */ public function i_send_message_to_user($messagecontent, $userfullname) { /** @var behat_forms $form */ $form = behat_context_helper::get('behat_forms'); /* @var behat_general $general */ $general = behat_context_helper::get('behat_general'); $this->getSession()->visit($this->locate_path('message')); $form->i_set_the_field_to(get_string('searchcombined', 'message'), $this->escape($userfullname)); $general->i_click_on('input[name="combinedsubmit"]', 'css_element'); $general->click_link($this->escape(get_string('sendmessageto', 'message', $userfullname))); $form->i_set_the_field_to('id_message', $this->escape($messagecontent)); $general->i_click_on('#id_submitbutton', 'css_element'); }