Skip to content

Commit

Permalink
Add behat test for preview - keyboard and fix is_complete_response
Browse files Browse the repository at this point in the history
  • Loading branch information
AnupamaSarjoshi committed Oct 21, 2024
1 parent 3b92818 commit b92df1f
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 53 deletions.
2 changes: 1 addition & 1 deletion amd/build/line.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion amd/build/line.min.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion amd/build/question.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion amd/build/question.min.js.map

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions amd/src/line.js
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,6 @@ define(function() {
selectedElement.childNodes[1].setAttribute('tabindex', '-1');
selectedElement.childNodes[2].setAttribute('tabindex', '-1');
}
return '';
};

/**
Expand Down Expand Up @@ -600,9 +599,9 @@ define(function() {
*/
function createSvgShapeGroup(svg, tagName) {
var svgEl = createSvgElement(svg, 'g');
svgEl.setAttribute('tabindex', '0');
var lineEl = createSvgElement(svgEl, tagName);
lineEl.setAttribute('class', 'shape');
lineEl.setAttribute('tabindex', '0');
var startcircleEl = createSvgElement(svgEl, 'circle');
startcircleEl.setAttribute('class', 'startcircle shape');
var endcirleEl = createSvgElement(svgEl, 'circle');
Expand Down Expand Up @@ -665,8 +664,8 @@ define(function() {
var linestartbits = startcoordinates[0].split(',');
var lineendbits = endcoordinates[0].split(',');

return new Line(labels[0], linestartbits[0], linestartbits[1], startcoordinates[1], labels[1],
lineendbits[0], lineendbits[1], endcoordinates[1], lineType);
return new Line(labels[0], parseInt(linestartbits[0]), parseInt(linestartbits[1]), parseInt(startcoordinates[1]),
labels[1], parseInt(lineendbits[0]), parseInt(lineendbits[1]), parseInt(endcoordinates[1]), lineType);
},

/**
Expand Down
59 changes: 25 additions & 34 deletions amd/src/question.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,13 @@ define([
}
thisQ.allImagesLoaded = false;
// Get all images that are not yet loaded
const images = thisQ.getNotYetLoadedImages();

// Loop over each image and add an event listener for the 'load' event
if (images) {
images.forEach((imgNode) => {
imgNode.addEventListener('load', function() {
thisQ.waitForAllImagesToBeLoaded();
}, {once: true}); // The { once: true } option ensures the listener is called only once
thisQ.waitForAllImagesToBeLoaded()
.then(() => {
thisQ.drawDropzone(); // Call your function here
})
.catch((error) => {
throw error;
});
}
thisQ.waitForAllImagesToBeLoaded();
}

/**
Expand Down Expand Up @@ -731,8 +727,7 @@ define([
dropzoneElement,
question = questionManager.getQuestionForEvent(e);

dropzoneElement = event.target.closest('g');

dropzoneElement = drag.closest('g');
switch (e.code) {
case 'ArrowLeft':
case 'KeyA': // A.
Expand Down Expand Up @@ -787,10 +782,10 @@ define([

if (activeElement === 'line') {
// Move the entire line when the focus is on it.
question.lines[dropzoneNo].moveDrags(x, y, parseInt(maxX), parseInt(maxY), whichSVG);
question.lines[dropzoneNo].moveDrags(parseInt(x), parseInt(y), parseInt(maxX), parseInt(maxY), whichSVG);
} else {
// Move the line endpoints.
question.lines[dropzoneNo].move(activeElement, x, y, parseInt(maxX), parseInt(maxY));
question.lines[dropzoneNo].move(activeElement, parseInt(x), parseInt(y), parseInt(maxX), parseInt(maxY));
}
question.updateSvgEl(dropzoneNo);
this.saveCoordsForChoice(dropzoneNo);
Expand Down Expand Up @@ -842,6 +837,8 @@ define([
*
* This function is called from the onLoad of each image, and also polls with
* a time-out, because image on-loads are allegedly unreliable.
*
* @return {Promise<void>}
*/
DrawlinesQuestion.prototype.waitForAllImagesToBeLoaded = function() {

Expand All @@ -850,26 +847,20 @@ define([
if (this.allImagesLoaded) {
return;
}
const images = document.querySelectorAll('img');
const promises = Array.from(images).map((img) => {
return new Promise((resolve, reject) => {
if (img.complete) {
resolve(); // If image is already loaded
} else {
img.onload = () => resolve();
img.onerror = () => reject(new Error(`Failed to load image: ${img.src}`));
}
});
});

// Clear any current timeout, if set.
if (this.imageLoadingTimeoutId !== null) {
clearTimeout(this.imageLoadingTimeoutId);
}

// If we have not yet loaded all images, set a timeout to
// call ourselves again, since apparently images on-load
// events are flakey.
const images = this.getNotYetLoadedImages();
if (images && images.length > 0) {
this.imageLoadingTimeoutId = setTimeout(function() {
this.waitForAllImagesToBeLoaded();
}, 100);
return;
}

// We now have all images. Carry on, but only after giving the layout a chance to settle down.
this.allImagesLoaded = true;
this.drawDropzone();
return Promise.all(promises);
};

/**
Expand Down Expand Up @@ -1070,8 +1061,8 @@ define([
dropzoneElement = e.target.closest('.dropzone');
dropzoneNo = dropzoneElement.dataset.dropzoneNo;
activeElement = 'endcircle';
} else if (e.target.closest('.dropzone polyline.shape')) {
drag = e.target.closest('.dropzone polyline.shape');
} else if (e.target.closest('g.dropzone')) {
drag = e.target.closest('g.dropzone');
dropzoneElement = e.target.closest('.dropzone');
dropzoneNo = dropzoneElement.dataset.dropzoneNo;
activeElement = 'line';
Expand Down
20 changes: 15 additions & 5 deletions classes/line.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,15 @@ class line {
/** @var string validate-zone-coordinates for start and the end of the line */
const VALIDATE_ZONE_COORDINATES = "/^([0-9]+),([0-9]+);([0-9]+)$/";

/** @var string validate-response-coordinates for a line
* as the start(scx,scy) and the end(ecx,ecy) coordinates of the line in 'scx,scy ecx,ecy' format.
/** @var string validate-response-coordinates for a line of type linesegment, linesinglearrow, linedoublearrows.
* as the start(x1,y1) and the end(x2,y2) coordinates of the line in 'x1,y1 x2,y2' format.
*/
const VALIDATE_RESPONSE_COORDINATES = "/^([^-][0-9]+),([^-][0-9]+)\b([^-][0-9]+),([^-][0-9]+)$/";
const VALIDATE_RESPONSE_COORDINATES = "/^(\d+,\d+)( \d+,\d+)$/";

/** @var string validate-response-coordinates for infinite line.
* as the coordinates of the line in the format 'x1,y1 x2,y2 x3,y3 x4,y4' format.
*/
const VALIDATE_INFINITE_RESPONSE_COORDINATES = "/^(\d+,\d+)( \d+,\d+)( \d+,\d+)( \d+,\d+)$/";

/** @var int The line id. */
public $id;
Expand Down Expand Up @@ -219,14 +224,19 @@ public static function is_zone_coordinates_valid(string $zone): bool {
* the start zone and ecx,ecy are the end zone coordinates of a line respectively.
*
* @param string $linecoordinates the coordinates for start and end of the line in 'scx,scy ecx,ecy' format.
* @param string $lineType the type of the line.
* @return bool
*/
public static function are_response_coordinates_valid(string $linecoordinates): bool {
public static function are_response_coordinates_valid(string $linecoordinates, string $lineType): bool {
// If the line-coordinates is empty return false.
if (trim($linecoordinates) === '') {
return false;
}
preg_match_all(self::VALIDATE_RESPONSE_COORDINATES, $linecoordinates, $matches, PREG_SPLIT_NO_EMPTY);
if ($lineType == 'lineinfinite') {
preg_match_all(self::VALIDATE_INFINITE_RESPONSE_COORDINATES, $linecoordinates, $matches, PREG_SPLIT_NO_EMPTY);
} else {
preg_match_all(self::VALIDATE_RESPONSE_COORDINATES, $linecoordinates, $matches, PREG_SPLIT_NO_EMPTY);
}

// If there is no match return false.
foreach ($matches as $i => $match) {
Expand Down
2 changes: 1 addition & 1 deletion question.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public function is_complete_response(array $response): bool {
}
foreach ($this->lines as $key => $line) {
if (isset($response[$this->choice($key)]) &&
!line::are_response_coordinates_valid($response[$this->choice($key)])) {
!line::are_response_coordinates_valid($response[$this->choice($key)], $line->type)) {
return false;
}
}
Expand Down
2 changes: 1 addition & 1 deletion styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ form.mform fieldset#id_previewareaheader .dropbackground {
}
.que.drawlines .dropzone .startcircle:focus,
.que.drawlines .dropzone .endcircle:focus,
.que.drawlines .dropzone polyline:focus {
.que.drawlines g.dropzone:focus {
outline: 2px solid #facb4e;
}

Expand Down
66 changes: 66 additions & 0 deletions tests/behat/behat_qtype_drawlines.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
/**
* Steps definitions related with the drawlines question type.
*
* @package qtype_drawlines
* @category test
* @copyright 2024 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_qtype_drawlines extends behat_base {

/**
* Get the xpath for a given drag item.
*
* @param string $line the line to drag.
* @param string $part part of the line being dragged.
* @param bool $iskeyboard is using keyboard or not.
* @return string the xpath expression.
*/
protected function line_xpath($line, $part, $iskeyboard = false) {
$lineNo = (int)$line - 1;
if ($iskeyboard) {
if ($part == 'line') {
return '//*[name()="svg"]/*[name()="g" and contains(@class, "choice' . $this->escape($lineNo) . '")]';
} else {
return '//*[name()="svg"]/*[name()="g" and contains(@class, "choice' . $this->escape($lineNo) . '")]' .
'/*[name()="circle" and contains(@class, "' . $this->escape($part) . '")]';
}
}
}

/**
* Type some characters while focused on a given line.
*
* @param string $direction the direction key to press.
* @param int $
* @param string $part the part of the line to move.
* @param string $line the line to drag. The label, optionally followed by ,<instance number> (int) if relevant.
*
* @Given /^I type "(?P<direction>up|down|left|right)" "(?P<repeats>\d+)" times on line "(?P<line>\d+)" "(?P<endpoint>line|startcircle|endcircle)" in the drawlines question$/
*/
public function i_type_on_line_in_the_drawlines_question($direction, $repeats, $line, $part) {
$node = $this->get_selected_node('xpath_element', $this->line_xpath($line, $part, true));
$this->ensure_node_is_visible($node);
$node->focus();
for ($i = 0; $i < $repeats; $i++) {
$this->execute('behat_general::i_press_named_key', ['', $direction]);
}
}
}
14 changes: 10 additions & 4 deletions tests/behat/preview.feature
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,14 @@ Feature: Preview a DrawLines question
And the following "questions" exist:
| questioncategory | qtype | name | template |
| Test questions | drawlines | Drawlines to preview | mkmap_twolines |
@javascript @_bug_phantomjs
Scenario: Preview a question using the mouse

@javascript
Scenario: Preview a question using the keyboard
When I am on the "Drawlines to preview" "core_question > preview" page logged in as teacher
And I pause
# TODO: Finishing this scenario after Js completed and adding other possible scenarios.
And I type "up" "360" times on line "1" "line" in the drawlines question
And I type "left" "40" times on line "1" "line" in the drawlines question
And I type "down" "190" times on line "1" "endcircle" in the drawlines question
And I type "left" "200" times on line "1" "endcircle" in the drawlines question
And I press "Submit and finish"
Then the state of "Draw 2 lines on the map" question is shown as "Partially correct"
And I should see "Mark 0.50 out of 1.00"

0 comments on commit b92df1f

Please sign in to comment.