/** * Internal step definition to find exceptions, debugging() messages and PHP debug messages. * * Part of behat_hooks class as is part of the testing framework, is auto-executed * after each step so no features will splicitly use it. * * @throws Exception Unknown type, depending on what we caught in the hook or basic \Exception. * @see Moodle\BehatExtension\Tester\MoodleStepTester */ public function look_for_exceptions() { // Wrap in try in case we were interacting with a closed window. try { // Exceptions. $exceptionsxpath = "//div[@data-rel='fatalerror']"; // Debugging messages. $debuggingxpath = "//div[@data-rel='debugging']"; // PHP debug messages. $phperrorxpath = "//div[@data-rel='phpdebugmessage']"; // Any other backtrace. $othersxpath = "(//*[contains(., ': call to ')])[1]"; $xpaths = array($exceptionsxpath, $debuggingxpath, $phperrorxpath, $othersxpath); $joinedxpath = implode(' | ', $xpaths); // Joined xpath expression. Most of the time there will be no exceptions, so this pre-check // is faster than to send the 4 xpath queries for each step. if (!$this->getSession()->getDriver()->find($joinedxpath)) { // Check if we have recorded any errors in driver process. $phperrors = behat_get_shutdown_process_errors(); if (!empty($phperrors)) { foreach ($phperrors as $error) { $errnostring = behat_get_error_string($error['type']); $msgs[] = $errnostring . ": " . $error['message'] . " at " . $error['file'] . ": " . $error['line']; } $msg = "PHP errors found:\n" . implode("\n", $msgs); throw new \Exception(htmlentities($msg)); } return; } // Exceptions. if ($errormsg = $this->getSession()->getPage()->find('xpath', $exceptionsxpath)) { // Getting the debugging info and the backtrace. $errorinfoboxes = $this->getSession()->getPage()->findAll('css', 'div.alert-error'); // If errorinfoboxes is empty, try find alert-danger (bootstrap4) class. if (empty($errorinfoboxes)) { $errorinfoboxes = $this->getSession()->getPage()->findAll('css', 'div.alert-danger'); } // If errorinfoboxes is empty, try find notifytiny (original) class. if (empty($errorinfoboxes)) { $errorinfoboxes = $this->getSession()->getPage()->findAll('css', 'div.notifytiny'); } // If errorinfoboxes is empty, try find ajax/JS exception in dialogue. if (empty($errorinfoboxes)) { $errorinfoboxes = $this->getSession()->getPage()->findAll('css', 'div.moodle-exception-message'); // If ajax/JS exception. if ($errorinfoboxes) { $errorinfo = $this->get_debug_text($errorinfoboxes[0]->getHtml()); } } else { $errorinfo = $this->get_debug_text($errorinfoboxes[0]->getHtml()) . "\n" . $this->get_debug_text($errorinfoboxes[1]->getHtml()); } $msg = "Moodle exception: " . $errormsg->getText() . "\n" . $errorinfo; throw new \Exception(html_entity_decode($msg)); } // Debugging messages. if ($debuggingmessages = $this->getSession()->getPage()->findAll('xpath', $debuggingxpath)) { $msgs = array(); foreach ($debuggingmessages as $debuggingmessage) { $msgs[] = $this->get_debug_text($debuggingmessage->getHtml()); } $msg = "debugging() message/s found:\n" . implode("\n", $msgs); throw new \Exception(html_entity_decode($msg)); } // PHP debug messages. if ($phpmessages = $this->getSession()->getPage()->findAll('xpath', $phperrorxpath)) { $msgs = array(); foreach ($phpmessages as $phpmessage) { $msgs[] = $this->get_debug_text($phpmessage->getHtml()); } $msg = "PHP debug message/s found:\n" . implode("\n", $msgs); throw new \Exception(html_entity_decode($msg)); } // Any other backtrace. // First looking through xpath as it is faster than get and parse the whole page contents, // we get the contents and look for matches once we found something to suspect that there is a backtrace. if ($this->getSession()->getDriver()->find($othersxpath)) { $backtracespattern = '/(line [0-9]* of [^:]*: call to [\\->&;:a-zA-Z_\\x7f-\\xff][\\->&;:a-zA-Z0-9_\\x7f-\\xff]*)/'; if (preg_match_all($backtracespattern, $this->getSession()->getPage()->getContent(), $backtraces)) { $msgs = array(); foreach ($backtraces[0] as $backtrace) { $msgs[] = $backtrace . '()'; } $msg = "Other backtraces found:\n" . implode("\n", $msgs); throw new \Exception(htmlentities($msg)); } } } catch (NoSuchWindow $e) { // If we were interacting with a popup window it will not exists after closing it. } }
/** * Before shutdown save last error entries, so we can fail the test. */ function behat_shutdown_function() { // If any error found, then save it. if ($error = error_get_last()) { // Ignore E_WARNING, as they might come via ( @ )suppression and might lead to false failure. if (isset($error['type']) && !($error['type'] & E_WARNING)) { $errors = behat_get_shutdown_process_errors(); $errors[] = $error; $errorstosave = json_encode($errors); set_config('process_errors', $errorstosave, 'tool_behat'); } } }