Skip to content

Commit

Permalink
test: use testable_question_attempt instead of stubbing
Browse files Browse the repository at this point in the history
  • Loading branch information
MHajoha committed Dec 11, 2024
1 parent 8f57d47 commit 8253124
Showing 1 changed file with 60 additions and 57 deletions.
117 changes: 60 additions & 57 deletions tests/attempt_ui/question_ui_renderer_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@
defined('MOODLE_INTERNAL') || die();

global $CFG;
require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
require_once($CFG->dirroot . '/question/type/questionpy/question.php');

use coding_exception;
use PHPUnit\Framework\MockObject\Stub;
use qtype_questionpy\api\api;
use qtype_questionpy\constants;
use qtype_questionpy_question;
use question_attempt;
use question_attempt_step;
use testable_question_attempt;

/**
* Unit tests for {@see question_ui_renderer}.
Expand Down Expand Up @@ -80,7 +84,7 @@ public function test_should_hide_inline_feedback(): void {
<div xmlns="http://www.w3.org/1999/xhtml">
<span>No feedback</span>
</div>
EXPECTED, $result);
EXPECTED, $result->html);
}

/**
Expand All @@ -104,7 +108,7 @@ public function test_should_show_inline_feedback(): void {
<span>General feedback</span>
<span>Specific feedback</span>
</div>
EXPECTED, $result);
EXPECTED, $result->html);
}

/**
Expand All @@ -122,27 +126,27 @@ public function test_should_mangle_names(): void {
$result = $ui->render();

$this->assert_html_string_equals_html_string(<<<EXPECTED
<div xmlns="http://www.w3.org/1999/xhtml" id="mangled:my_div">
<datalist id="mangled:my_list">
<div xmlns="http://www.w3.org/1999/xhtml" id="q1:1_my_div">
<datalist id="q1:1_my_list">
<option>42</option>
</datalist>
<label>Wrapping label <input class="form-control qpy-input" name="mangled:my_number"
type="number" list="mangled:my_list"/></label>
<label for="mangled:my_select">Separate label</label>
<select class="form-control qpy-input" id="mangled:my_select" name="mangled:my_select">
<label>Wrapping label <input class="form-control qpy-input" name="q1:1_my_number"
type="number" list="q1:1_my_list"/></label>
<label for="q1:1_my_select">Separate label</label>
<select class="form-control qpy-input" id="q1:1_my_select" name="q1:1_my_select">
<option value="1">One</option>
<option value="2">Two</option>
</select>
<input class="qpy-input" type="radio" name="mangled:my_radio" value="1"/>
<input class="qpy-input" type="radio" name="mangled:my_radio" value="2"/>
<textarea class="form-control qpy-input" name="mangled:my_text"/>
<button class="btn btn-primary qpy-input" name="mangled:my_button">Click me!</button>
<map name="mangled:my_map">
<input class="qpy-input" type="radio" name="q1:1_my_radio" value="1"/>
<input class="qpy-input" type="radio" name="q1:1_my_radio" value="2"/>
<textarea class="form-control qpy-input" name="q1:1_my_text"/>
<button class="btn btn-primary qpy-input" name="q1:1_my_button">Click me!</button>
<map name="q1:1_my_map">
<area shape="circle" coords="1, 2, 3"/>
</map>
<img src="https://picsum.photos/200/300" usemap="#mangled:my_map"/>
<img src="https://picsum.photos/200/300" usemap="#q1:1_my_map"/>
</div>
EXPECTED, $result);
EXPECTED, $result->html);
}

/**
Expand Down Expand Up @@ -179,7 +183,7 @@ public function test_should_shuffle_correctly_and_replace_indices(): void {
</div>
</div>
</div>
EXPECTED, $result);
EXPECTED, $result->html);
}

/**
Expand All @@ -195,7 +199,7 @@ public function test_should_shuffle_the_same_way_in_same_attempt(): void {
$firstresult = (new question_ui_renderer($input, [], new \question_display_options(), $qa))->render();
for ($i = 0; $i < 10; $i++) {
$result = (new question_ui_renderer($input, [], new \question_display_options(), $qa))->render();
$this->assertEquals($firstresult, $result);
$this->assertEquals($firstresult->html, $result->html);
}
}

Expand Down Expand Up @@ -225,7 +229,7 @@ public function test_should_resolve_placeholders(): void {
<span>Plain parameter: Value of param &lt;b>one&lt;/b>.&lt;script>'Oh no, danger!'&lt;/script>
</span>
</div>
EXPECTED, $result);
EXPECTED, $result->html);
}

/**
Expand Down Expand Up @@ -257,7 +261,7 @@ public function test_should_correctly_handle_broken_html_in_placeholder_expansio
<span>Noclean parameter: <format-float>123</format-float><unknown-tag></unknown-tag><div>unclosed</div></span>
<span>Plain parameter: &lt;qpy:format-float>123&lt;/qpy:format-float>&lt;unknown-tag>&lt;/unknown-tag>&lt;div>unclosed</span>
</div>
EXPECTED, $result);
EXPECTED, $result->html);
// phpcs:enable moodle.Files.LineLength.TooLong
}

Expand All @@ -283,7 +287,7 @@ public function test_should_remove_placeholders_when_no_corresponding_value(): v
<span>Noclean parameter: </span>
<span>Plain parameter: </span>
</div>
EXPECTED, $result);
EXPECTED, $result->html);
}

/**
Expand Down Expand Up @@ -312,7 +316,7 @@ public function test_should_soften_validations(): void {
data-qpy_minlength="5" data-qpy_maxlength="10"
aria-valuemin="17" data-qpy_min="17" aria-valuemax="42" data-qpy_max="42"/>
</div>
EXPECTED, $result);
EXPECTED, $result->html);
}

/**
Expand All @@ -338,7 +342,7 @@ public function test_should_defuse_buttons(): void {
<input class="btn btn-primary qpy-input" type="button" value="Reset"/>
<input class="btn btn-primary qpy-input" type="button" value="Button"/>
</div>
EXPECTED, $result);
EXPECTED, $result->html);
}

/**
Expand All @@ -363,7 +367,7 @@ public function test_should_remove_element_with_if_role_attribute(): void {

$this->assert_html_string_equals_html_string(<<<EXPECTED
<div xmlns="http://www.w3.org/1999/xhtml"></div>
EXPECTED, $result);
EXPECTED, $result->html);
}

/**
Expand Down Expand Up @@ -395,7 +399,7 @@ public function test_should_not_remove_element_with_if_role_attribute(): void {
<div>You're a proctor!</div>
<div>You're any of the above!</div>
</div>
EXPECTED, $result);
EXPECTED, $result->html);
}

/**
Expand All @@ -421,7 +425,7 @@ public function test_should_format_floats_in_en(): void {
Pad with zeros: 1.10000
Strip zeros: 1.1
</div>
EXPECTED, $result);
EXPECTED, $result->html);
}

/**
Expand All @@ -444,7 +448,7 @@ public function test_should_replace_qpy_urls(): void {
static link: <a href="https://www.example.com/moodle/pluginfile.php//qtype_questionpy/static/deadbeef/local/minimal_example/path1/path2/filename.txt">https://www.example.com/moodle/pluginfile.php//qtype_questionpy/static/deadbeef/local/minimal_example/path1/path2/filename.txt</a>
minimal path: <a href="https://www.example.com/moodle/pluginfile.php//qtype_questionpy/static/deadbeef/local/minimal_example/f">https://www.example.com/moodle/pluginfile.php//qtype_questionpy/static/deadbeef/local/minimal_example/f</a>
</div>
EXPECTED, $result);
EXPECTED, $result->html);
// phpcs:enable moodle.Files.LineLength.MaxExceeded
}

Expand All @@ -457,44 +461,41 @@ public function test_should_replace_qpy_urls(): void {
*/
public function test_should_correctly_fill_data(): void {
$input = file_get_contents(__DIR__ . '/question_uis/input-values.xhtml');
$qa = $this->create_question_attempt_stub('deadbeef');
$newvalues = [
$qa = $this->create_question_attempt_stub('deadbeef', lastresponse: [
'my_text' => 'new',
'my_checkbox_value' => 'value',
'my_checkbox_on' => 'on',
'my_radio' => 'value1',
'my_select' => 'value3',
'my_hidden' => 'new',
'my_button' => 'should be ignored',
];
$qa->method('get_last_qt_var')
->willReturnCallback(fn($name) => $newvalues[$name]);
]);

$ui = new question_ui_renderer($input, [], new \question_display_options(), $qa);
$result = $ui->render();

$this->assert_html_string_equals_html_string(<<<EXPECTED
<div xmlns="http://www.w3.org/1999/xhtml" id="mangled:my_div">
<input class="form-control qpy-input" type="text" name="mangled:my_text" value="new"/>
<div xmlns="http://www.w3.org/1999/xhtml" id="q1:1_my_div">
<input class="form-control qpy-input" type="text" name="q1:1_my_text" value="new"/>
<input class="qpy-input" type="checkbox" name="mangled:my_checkbox_value" value="value" checked="checked"/>
<input class="qpy-input" type="checkbox" name="mangled:my_checkbox_on" checked="checked"/>
<input class="qpy-input" type="checkbox" name="q1:1_my_checkbox_value" value="value" checked="checked"/>
<input class="qpy-input" type="checkbox" name="q1:1_my_checkbox_on" checked="checked"/>
<input class="qpy-input" type="radio" name="mangled:my_radio" value="value1" checked="checked"/>
<input class="qpy-input" type="radio" name="mangled:my_radio" value="value2"/>
<input class="qpy-input" type="radio" name="q1:1_my_radio" value="value1" checked="checked"/>
<input class="qpy-input" type="radio" name="q1:1_my_radio" value="value2"/>
<select class="form-control qpy-input" name="mangled:my_select">
<select class="form-control qpy-input" name="q1:1_my_select">
<option value="value1"/>
<option value="value2"/>
<option value="value3" selected="selected"/>
</select>
<input class="form-control qpy-input" type="hidden" name="mangled:my_hidden" value="new"/>
<input class="form-control qpy-input" type="hidden" name="q1:1_my_hidden" value="new"/>
<input class="btn btn-primary qpy-input" name="mangled:my_button" type="button" value="value1"/>
<input class="btn btn-primary qpy-input" name="mangled:my_button" type="button" value="value2"/>
<input class="btn btn-primary qpy-input" name="q1:1_my_button" type="button" value="value1"/>
<input class="btn btn-primary qpy-input" name="q1:1_my_button" type="button" value="value2"/>
</div>
EXPECTED, $result);
EXPECTED, $result->html);
}

/**
Expand All @@ -506,23 +507,23 @@ public function test_should_correctly_fill_data(): void {
*/
public function test_should_add_fallback_option_to_select_when_value_isnt_present(): void {
$input = file_get_contents(__DIR__ . '/question_uis/select.xhtml');
$qa = $this->create_question_attempt_stub('deadbeef');
$qa->method('get_last_qt_var')
->willReturn('something');
$qa = $this->create_question_attempt_stub('deadbeef', lastresponse: [
'my_select' => 'something',
]);

$ui = new question_ui_renderer($input, [], new \question_display_options(), $qa);
$result = $ui->render();

$this->assert_html_string_equals_html_string(<<<EXPECTED
<div xmlns="http://www.w3.org/1999/xhtml" >
<select class="form-control qpy-input" name="mangled:my_select">
<select class="form-control qpy-input" name="q1:1_my_select">
<option value="value1"/>
<option value="value2"/>
<option value="value3"/>
<option value="something" selected="selected">(the selected option is no longer available)</option>
</select>
</div>
EXPECTED, $result);
EXPECTED, $result->html);
}

/**
Expand All @@ -531,21 +532,23 @@ public function test_should_add_fallback_option_to_select_when_value_isnt_presen
* @param string|null $packagehash explicit package hash. Random if unset.
* @param int|null $id explicit attempt database id. Random if unset.
* @return question_attempt&Stub
* @throws coding_exception
*/
private function create_question_attempt_stub(?string $packagehash = null, ?int $id = null): question_attempt {
private function create_question_attempt_stub(?string $packagehash = null, ?int $id = null,
array $lastresponse = []): question_attempt {
$packagehash ??= hash('sha256', random_string(64));
$id ??= mt_rand();
$question = new qtype_questionpy_question($packagehash, '{}', null, $this->createStub(api::class));

$qa = $this->createStub(question_attempt::class);
$qa->method('get_database_id')
->willReturn($id);
$qa->method('get_question')
->willReturn($question);
$qa->method('get_qt_field_name')
->willReturnCallback(function ($name) {
return "mangled:$name";
});
$step = new question_attempt_step(array_merge($lastresponse, [
constants::QT_VAR_RESPONSE_MARKER => '',
]));

$qa = new testable_question_attempt($question, 1);
$qa->set_database_id($id);
$qa->set_slot(1);
$qa->add_step($step);

return $qa;
}
}

0 comments on commit 8253124

Please sign in to comment.