mirror of
https://github.com/moodle/moodle.git
synced 2025-04-13 12:32:08 +02:00
MDL-63185 mod_quiz: more fixes to new behat code
Part of MDL-62610
This commit is contained in:
parent
9b1fc262d8
commit
c5499edaef
@ -44,19 +44,17 @@ Feature: Basic use of the Responses report
|
||||
|
||||
@javascript
|
||||
Scenario: Report works when there are attempts
|
||||
Given I log in as "student1"
|
||||
And user "student1" has started an attempt at quiz "Quiz 1"
|
||||
And user "student1" has submitted answers in their attempt at quiz "Quiz 1":
|
||||
Given user "student1" has started an attempt at quiz "Quiz 1"
|
||||
And user "student1" has checked answers in their attempt at quiz "Quiz 1":
|
||||
| slot | response |
|
||||
| 1 | 1.0 |
|
||||
And user "student1" has submitted answers in their attempt at quiz "Quiz 1":
|
||||
And user "student1" has checked answers in their attempt at quiz "Quiz 1":
|
||||
| slot | response |
|
||||
| 1 | 3.0 |
|
||||
And user "student1" has submitted answers in their attempt at quiz "Quiz 1":
|
||||
And user "student1" has checked answers in their attempt at quiz "Quiz 1":
|
||||
| slot | response |
|
||||
| 1 | 3.14 |
|
||||
And user "student1" has finished an attempt at quiz "Quiz 1"
|
||||
And I log out
|
||||
When I log in as "teacher"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Quiz 1"
|
||||
|
@ -562,6 +562,60 @@ class behat_mod_quiz extends behat_question_base {
|
||||
$this->find('xpath', $xpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper used by user_has_attempted_with_responses,
|
||||
* user_has_started_an_attempt_at_quiz_with_details, etc.
|
||||
*
|
||||
* @param TableNode $attemptinfo data table from the Behat step
|
||||
* @return array with two elements, $forcedrandomquestions, $forcedvariants,
|
||||
* that can be passed to $quizgenerator->create_attempt.
|
||||
*/
|
||||
protected function extract_forced_randomisation_from_attempt_info(TableNode $attemptinfo) {
|
||||
global $DB;
|
||||
|
||||
$forcedrandomquestions = [];
|
||||
$forcedvariants = [];
|
||||
foreach ($attemptinfo->getHash() as $slotinfo) {
|
||||
if (empty($slotinfo['slot'])) {
|
||||
throw new ExpectationException('When simulating a quiz attempt, ' .
|
||||
'the slot column is required.', $this->getSession());
|
||||
}
|
||||
|
||||
if (!empty($slotinfo['actualquestion'])) {
|
||||
$forcedrandomquestions[$slotinfo['slot']] = $DB->get_field('question', 'id',
|
||||
['name' => $slotinfo['actualquestion']], MUST_EXIST);
|
||||
}
|
||||
|
||||
if (!empty($slotinfo['variant'])) {
|
||||
$forcedvariants[$slotinfo['slot']] = (int) $slotinfo['variant'];
|
||||
}
|
||||
}
|
||||
return [$forcedrandomquestions, $forcedvariants];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper used by user_has_attempted_with_responses, user_has_checked_answers_in_their_attempt_at_quiz,
|
||||
* user_has_input_answers_in_their_attempt_at_quiz, etc.
|
||||
*
|
||||
* @param TableNode $attemptinfo data table from the Behat step
|
||||
* @return array of responses that can be passed to $quizgenerator->submit_responses.
|
||||
*/
|
||||
protected function extract_responses_from_attempt_info(TableNode $attemptinfo) {
|
||||
$responses = [];
|
||||
foreach ($attemptinfo->getHash() as $slotinfo) {
|
||||
if (empty($slotinfo['slot'])) {
|
||||
throw new ExpectationException('When simulating a quiz attempt, ' .
|
||||
'the slot column is required.', $this->getSession());
|
||||
}
|
||||
if (!array_key_exists('response', $slotinfo)) {
|
||||
throw new ExpectationException('When simulating a quiz attempt, ' .
|
||||
'the response column is required.', $this->getSession());
|
||||
}
|
||||
$responses[$slotinfo['slot']] = $slotinfo['response'];
|
||||
}
|
||||
return $responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt a quiz.
|
||||
*
|
||||
@ -603,36 +657,16 @@ class behat_mod_quiz extends behat_question_base {
|
||||
$quizid = $DB->get_field('quiz', 'id', ['name' => $quizname], MUST_EXIST);
|
||||
$user = $DB->get_record('user', ['username' => $username], '*', MUST_EXIST);
|
||||
|
||||
$forcedrandomquestions = [];
|
||||
$forcedvariants = [];
|
||||
$responses = [];
|
||||
foreach ($attemptinfo->getHash() as $slotinfo) {
|
||||
if (empty($slotinfo['slot'])) {
|
||||
throw new ExpectationException('When simulating a quiz attempt, ' .
|
||||
'the slot column is required.', $this->getSession());
|
||||
}
|
||||
if (!array_key_exists('response', $slotinfo)) {
|
||||
throw new ExpectationException('When simulating a quiz attempt, ' .
|
||||
'the response column is required.', $this->getSession());
|
||||
}
|
||||
$responses[$slotinfo['slot']] = $slotinfo['response'];
|
||||
|
||||
if (!empty($slotinfo['actualquestion'])) {
|
||||
$forcedrandomquestions[$slotinfo['slot']] = $DB->get_field('question', 'id',
|
||||
['name' => $slotinfo['actualquestion']], MUST_EXIST);
|
||||
}
|
||||
|
||||
if (!empty($slotinfo['variant'])) {
|
||||
$forcedvariants[$slotinfo['slot']] = (int) $slotinfo['variant'];
|
||||
}
|
||||
}
|
||||
list($forcedrandomquestions, $forcedvariants) =
|
||||
$this->extract_forced_randomisation_from_attempt_info($attemptinfo);
|
||||
$responses = $this->extract_responses_from_attempt_info($attemptinfo);
|
||||
|
||||
$this->set_user($user);
|
||||
|
||||
$attempt = $quizgenerator->create_attempt($quizid, $user->id,
|
||||
$forcedrandomquestions, $forcedvariants);
|
||||
|
||||
$quizgenerator->submit_responses($attempt->id, $responses, true);
|
||||
$quizgenerator->submit_responses($attempt->id, $responses, false, true);
|
||||
|
||||
$this->set_user();
|
||||
}
|
||||
@ -662,15 +696,18 @@ class behat_mod_quiz extends behat_question_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit answers to an existing quiz attempt.
|
||||
* Start a quiz attempt without answers.
|
||||
*
|
||||
* The supplied data table for have a row for each slot where you want
|
||||
* to force either which random question was chose, or which random variant
|
||||
* was used, as for {@link user_has_attempted_with_responses()} above.
|
||||
*
|
||||
* @param string $username the username of the user that will attempt.
|
||||
* @param string $quizname the name of the quiz the user will attempt.
|
||||
* @param string $username the username of the user that will attempt.
|
||||
* @param TableNode $attemptinfo information about the questions to add, as above.
|
||||
* @throws \Behat\Mink\Exception\ExpectationException
|
||||
* @Given /^user "([^"]*)" has submitted answers in their attempt at quiz "([^"]*)":$/
|
||||
* @Given /^user "([^"]*)" has started an attempt at quiz "([^"]*) randomised as follows:"$/
|
||||
*/
|
||||
public function user_has_submitted_answers_in_their_attempt_at_quiz($username, $quizname, TableNode $attemptinfo) {
|
||||
public function user_has_started_an_attempt_at_quiz_with_details($username, $quizname, TableNode $attemptinfo) {
|
||||
global $DB;
|
||||
|
||||
/** @var mod_quiz_generator $quizgenerator */
|
||||
@ -679,30 +716,84 @@ class behat_mod_quiz extends behat_question_base {
|
||||
$quizid = $DB->get_field('quiz', 'id', ['name' => $quizname], MUST_EXIST);
|
||||
$user = $DB->get_record('user', ['username' => $username], '*', MUST_EXIST);
|
||||
|
||||
$forcedvariants = [];
|
||||
$responses = [];
|
||||
foreach ($attemptinfo->getHash() as $slotinfo) {
|
||||
if (empty($slotinfo['slot'])) {
|
||||
throw new ExpectationException('When simulating a quiz attempt, ' .
|
||||
'the slot column is required.', $this->getSession());
|
||||
}
|
||||
if (!array_key_exists('response', $slotinfo)) {
|
||||
throw new ExpectationException('When simulating a quiz attempt, ' .
|
||||
'the response column is required.', $this->getSession());
|
||||
}
|
||||
$responses[$slotinfo['slot']] = $slotinfo['response'];
|
||||
|
||||
if (!empty($slotinfo['variant'])) {
|
||||
$forcedvariants[$slotinfo['slot']] = (int) $slotinfo['variant'];
|
||||
}
|
||||
}
|
||||
list($forcedrandomquestions, $forcedvariants) =
|
||||
$this->extract_forced_randomisation_from_attempt_info($attemptinfo);
|
||||
|
||||
$this->set_user($user);
|
||||
|
||||
foreach (quiz_get_user_attempts($quizid, $user->id, 'unfinished', true) as $attemptid => $attemptobj) {
|
||||
$quizgenerator->submit_responses($attemptid, $responses, false);
|
||||
break;
|
||||
}
|
||||
$quizgenerator->create_attempt($quizid, $user->id,
|
||||
$forcedrandomquestions, $forcedvariants);
|
||||
|
||||
$this->set_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Input answers to particular questions an existing quiz attempt, without
|
||||
* simulating a click of the 'Check' button, if any.
|
||||
*
|
||||
* Then there should be a number of rows of data, with two columns slot and response,
|
||||
* as for {@link user_has_attempted_with_responses()} above.
|
||||
* There is no need to supply answers to all questions. If so, other questions will be
|
||||
* left unanswered.
|
||||
*
|
||||
* @param string $username the username of the user that will attempt.
|
||||
* @param string $quizname the name of the quiz the user will attempt.
|
||||
* @param TableNode $attemptinfo information about the questions to add, as above.
|
||||
* @throws \Behat\Mink\Exception\ExpectationException
|
||||
* @Given /^user "([^"]*)" has input answers in their attempt at quiz "([^"]*)":$/
|
||||
*/
|
||||
public function user_has_input_answers_in_their_attempt_at_quiz($username, $quizname, TableNode $attemptinfo) {
|
||||
global $DB;
|
||||
|
||||
/** @var mod_quiz_generator $quizgenerator */
|
||||
$quizgenerator = behat_util::get_data_generator()->get_plugin_generator('mod_quiz');
|
||||
|
||||
$quizid = $DB->get_field('quiz', 'id', ['name' => $quizname], MUST_EXIST);
|
||||
$user = $DB->get_record('user', ['username' => $username], '*', MUST_EXIST);
|
||||
|
||||
$responses = $this->extract_responses_from_attempt_info($attemptinfo);
|
||||
|
||||
$this->set_user($user);
|
||||
|
||||
$attempts = quiz_get_user_attempts($quizid, $user->id, 'unfinished', true);
|
||||
$quizgenerator->submit_responses(key($attempts), $responses, false, false);
|
||||
|
||||
$this->set_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit answers to questions an existing quiz attempt, with a simulated click on the 'Check' button.
|
||||
*
|
||||
* This step should only be used with question behaviours that have have
|
||||
* a 'Check' button. Those include Interactive with multiple tires, Immediate feedback
|
||||
* and Immediate feedback with CBM.
|
||||
*
|
||||
* Then there should be a number of rows of data, with two columns slot and response,
|
||||
* as for {@link user_has_attempted_with_responses()} above.
|
||||
* There is no need to supply answers to all questions. If so, other questions will be
|
||||
* left unanswered.
|
||||
*
|
||||
* @param string $quizname the name of the quiz the user will attempt.
|
||||
* @param string $username the username of the user that will attempt.
|
||||
* @param TableNode $attemptinfo information about the questions to add, as above.
|
||||
* @throws \Behat\Mink\Exception\ExpectationException
|
||||
* @Given /^user "([^"]*)" has checked answers in their attempt at quiz "([^"]*)":$/
|
||||
*/
|
||||
public function user_has_checked_answers_in_their_attempt_at_quiz($username, $quizname, TableNode $attemptinfo) {
|
||||
global $DB;
|
||||
|
||||
/** @var mod_quiz_generator $quizgenerator */
|
||||
$quizgenerator = behat_util::get_data_generator()->get_plugin_generator('mod_quiz');
|
||||
|
||||
$quizid = $DB->get_field('quiz', 'id', ['name' => $quizname], MUST_EXIST);
|
||||
$user = $DB->get_record('user', ['username' => $username], '*', MUST_EXIST);
|
||||
|
||||
$responses = $this->extract_responses_from_attempt_info($attemptinfo);
|
||||
|
||||
$this->set_user($user);
|
||||
|
||||
$attempts = quiz_get_user_attempts($quizid, $user->id, 'unfinished', true);
|
||||
$quizgenerator->submit_responses(key($attempts), $responses, true, false);
|
||||
|
||||
$this->set_user();
|
||||
}
|
||||
@ -722,11 +813,9 @@ class behat_mod_quiz extends behat_question_base {
|
||||
|
||||
$this->set_user($user);
|
||||
|
||||
foreach (quiz_get_user_attempts($quizid, $user->id, 'unfinished', true) as $attemptid => $attemptobj) {
|
||||
$attemptobj = quiz_attempt::create($attemptid);
|
||||
$attemptobj->process_finish(time(), true);
|
||||
break;
|
||||
}
|
||||
$attempts = quiz_get_user_attempts($quizid, $user->id, 'unfinished', true);
|
||||
$attemptobj = quiz_attempt::create(key($attempts));
|
||||
$attemptobj->process_finish(time(), true);
|
||||
|
||||
$this->set_user();
|
||||
}
|
||||
|
@ -121,8 +121,8 @@ class mod_quiz_generator extends testing_module_generator {
|
||||
'be able to create one attempt for each user. (This should be fixed.)');
|
||||
}
|
||||
|
||||
return quiz_prepare_and_start_new_attempt($quizobj, 1, null,
|
||||
$offlineattempt = false, $forcedrandomquestions = [], $forcedvariants);
|
||||
return quiz_prepare_and_start_new_attempt($quizobj, 1, null, false,
|
||||
$forcedrandomquestions, $forcedvariants);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -134,6 +134,8 @@ class mod_quiz_generator extends testing_module_generator {
|
||||
* @param int $attemptid the id of the attempt which is being
|
||||
* @param array $responses array responses to submit. See description on
|
||||
* {@link core_question_generator::get_simulated_post_data_for_questions_in_usage()}.
|
||||
* @param bool $checkbutton if simulate a click on the check button for each question, else simulate save.
|
||||
* This should only be used with behaviours that have a check button.
|
||||
* @param bool $finishattempt if true, the attempt will be submitted.
|
||||
*/
|
||||
public function submit_responses($attemptid, array $responses, $checkbutton, $finishattempt) {
|
||||
@ -142,10 +144,28 @@ class mod_quiz_generator extends testing_module_generator {
|
||||
$attemptobj = quiz_attempt::create($attemptid);
|
||||
|
||||
$postdata = $questiongenerator->get_simulated_post_data_for_questions_in_usage(
|
||||
$attemptobj->get_question_usage(), $responses);
|
||||
$attemptobj->get_question_usage(), $responses, $checkbutton);
|
||||
|
||||
$attemptobj->process_submitted_actions(time(), false, $postdata);
|
||||
|
||||
// Bit if a hack for interactive behaviour.
|
||||
// TODO handle this in a more plugin-friendly way.
|
||||
if ($checkbutton) {
|
||||
$postdata = [];
|
||||
foreach ($responses as $slot => $notused) {
|
||||
$qa = $attemptobj->get_question_attempt($slot);
|
||||
if ($qa->get_behaviour() instanceof qbehaviour_interactive && $qa->get_behaviour()->is_try_again_state()) {
|
||||
$postdata[$qa->get_control_field_name('sequencecheck')] = (string)$qa->get_sequence_check_count();
|
||||
$postdata[$qa->get_flag_field_name()] = (string)(int)$qa->is_flagged();
|
||||
$postdata[$qa->get_behaviour_field_name('tryagain')] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($postdata) {
|
||||
$attemptobj->process_submitted_actions(time(), false, $postdata);
|
||||
}
|
||||
}
|
||||
|
||||
if ($finishattempt) {
|
||||
$attemptobj->process_finish(time(), false);
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ class qbehaviour_interactive extends question_behaviour_with_multiple_tries {
|
||||
/**
|
||||
* @return bool are we are currently in the try_again state.
|
||||
*/
|
||||
protected function is_try_again_state() {
|
||||
public function is_try_again_state() {
|
||||
$laststep = $this->qa->get_last_step();
|
||||
return $this->qa->get_state()->is_active() && $laststep->has_behaviour_var('submit') &&
|
||||
$laststep->has_behaviour_var('_triesleft');
|
||||
|
@ -167,15 +167,17 @@ class core_question_generator extends component_generator_base {
|
||||
*
|
||||
* @param question_usage_by_activity $quba the question usage.
|
||||
* @param array $responses the resonses to submit, in the format described above.
|
||||
* @param bool $checkbutton if simulate a click on the check button for each question, else simulate save.
|
||||
* This should only be used with behaviours that have a check button.
|
||||
* @return array that can be passed to methods like $quba->process_all_actions as simulated POST data.
|
||||
*/
|
||||
public function get_simulated_post_data_for_questions_in_usage(
|
||||
question_usage_by_activity $quba, array $responses) {
|
||||
question_usage_by_activity $quba, array $responses, $checkbutton) {
|
||||
$postdata = [];
|
||||
|
||||
foreach ($responses as $slot => $responsesummary) {
|
||||
$postdata += $this->get_simulated_post_data_for_question_attempt(
|
||||
$quba->get_question_attempt($slot), $responsesummary);
|
||||
$quba->get_question_attempt($slot), $responsesummary, $checkbutton);
|
||||
}
|
||||
|
||||
return $postdata;
|
||||
@ -190,11 +192,13 @@ class core_question_generator extends component_generator_base {
|
||||
* are passed to the un_summarise_response method of the question to decode.
|
||||
*
|
||||
* @param question_attempt $qa the question attempt for which we are generating POST data.
|
||||
* @param string $responsesummary a textual summary of the resonse, as described above.
|
||||
* @return array the sumulated post data that can be passed to $quba->process_all_actions.
|
||||
* @param string $responsesummary a textual summary of the response, as described above.
|
||||
* @param bool $checkbutton if simulate a click on the check button, else simulate save.
|
||||
* This should only be used with behaviours that have a check button.
|
||||
* @return array the simulated post data that can be passed to $quba->process_all_actions.
|
||||
*/
|
||||
public function get_simulated_post_data_for_question_attempt(
|
||||
question_attempt $qa, $responsesummary) {
|
||||
question_attempt $qa, $responsesummary, $checkbutton) {
|
||||
|
||||
$question = $qa->get_question();
|
||||
if (!$question instanceof question_with_responses) {
|
||||
@ -210,7 +214,10 @@ class core_question_generator extends component_generator_base {
|
||||
$postdata[$qa->get_qt_field_name($name)] = (string)$value;
|
||||
}
|
||||
|
||||
// TODO handle behaviour variables.
|
||||
// TODO handle behaviour variables better than this.
|
||||
if ($checkbutton) {
|
||||
$postdata[$qa->get_behaviour_field_name('submit')] = 1;
|
||||
}
|
||||
|
||||
return $postdata;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user