MDL-84578 qbank_managecategories: Add category validation.

- This adds simple logic check to filter.js to stop user
submitting an invalid category.
- This adds new behat steps to check this validation.
- This adds a new behat test to test this in managecategories.
This commit is contained in:
Conn Warwicker
2025-03-10 14:06:47 +00:00
parent ae31be1e34
commit dd8b44fdb6
6 changed files with 121 additions and 5 deletions
+75
View File
@@ -834,4 +834,79 @@ class behat_forms extends behat_base {
);
}
}
/**
* Check that the validationMessage property on a form field element includes the given text.
*
* @Then the :field field validation message should contain :text
* @param string $field The css selector for the input field
* @param string $text The text which should be found in the validation message
*/
public function the_field_validation_message_should_contain(string $field, string $text): void {
// We can't use this assertion if javascript is not running.
$this->require_javascript();
// Check that the element exists.
// This is fail and go no further, if the element does not exist.
$node = $this->get_selected_node('field', $field);
// Get the validity result.
$wdelement = $this->get_webdriver_element_from_node_element($node);
$webdriver = $this->getSession()->getDriver()->getWebDriver();
$message = $webdriver->executeScript("return arguments[0].validationMessage;", [$wdelement]);
if (strpos($message, $text) === false) {
throw new ExpectationException(
'"' . $field . '" validation message does not contain "' . $text . '"', $this->getSession()
);
}
}
/**
* Check that the result of calling the checkValidity API on a form field element matches the expected result.
*
* @Then the :field field validity check should return :result
* @param string $field The css selector for the input field
* @param string $expected "true" or "false"
*/
public function the_field_validity_check_should_return(string $field, string $expected): void {
// We can't use this assertion if javascript is not running.
$this->require_javascript();
// Expected value can only be 'true' or 'false'.
$expected = strtolower($expected);
if (!in_array($expected, ['true', 'false'])) {
throw new ExpectationException(
'Invalid value for expected value "' . $expected . '". Should be "true" or "false".',
$this->getSession());
}
// Convert the expected result from a string to bool.
$expected = ($expected === "true");
// Check that the element exists.
// This is fail and go no further, if the element does not exist.
$node = $this->get_selected_node('field', $field);
// Get the validity result.
$wdelement = $this->get_webdriver_element_from_node_element($node);
$webdriver = $this->getSession()->getDriver()->getWebDriver();
$result = $webdriver->executeScript("return arguments[0].checkValidity();", [$wdelement]);
if ($result !== $expected) {
// Convert booleans to strings for the exception message.
$result = ($result) ? "true" : "false";
$expected = ($expected) ? "true" : "false";
throw new ExpectationException(
'"' . $field . '" validation check was "' . $result . '". Expected: "' .
$expected . '"', $this->getSession()
);
}
}
}
+2 -2
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+25 -2
View File
@@ -26,7 +26,7 @@ import Notification from 'core/notification';
import Selectors from 'core/datafilter/selectors';
import Templates from 'core/templates';
import Fragment from 'core/fragment';
import {get_strings as getStrings} from 'core/str';
/**
* Initialise the question bank filter on the element with the given id.
*
@@ -66,6 +66,8 @@ export const init = (
MENU_ACTIONS: '.menu-action',
EDIT_SWITCH: '.editmode-switch-form input[name=setmode]',
EDIT_SWITCH_URL: '.editmode-switch-form input[name=pageurl]',
CATEGORY_VALIDATION_INPUT: 'div[data-filter-type="category"] div.form-autocomplete-input input',
QUESTION_BANK_WINDOW: '.questionbankwindow',
};
const filterSet = document.querySelector(`#${filterRegionId}`);
@@ -97,6 +99,27 @@ export const init = (
* @param {Promise} pendingPromise pending promise
*/
const applyFilter = (filterdata, pendingPromise) => {
// MDL-84578 - This is a simple fix for older stable branches, which does not require
// backporting loads of functionality to validate filters properly.
let categoryid = parseInt(filterdata.category.values[0]);
let categorynode = document.querySelector(SELECTORS.CATEGORY_VALIDATION_INPUT);
categorynode.setCustomValidity('');
if (isNaN(categoryid) || categoryid <= 0) {
getStrings([
{
key: 'error:category',
component: 'qbank_managecategories',
},
]).then((strings) => {
categorynode.setCustomValidity(strings[0]);
categorynode.reportValidity();
return strings;
}).catch(Notification.exception);
pendingPromise.resolve();
return;
}
// Reload the questions based on the specified filters. If no filters are provided,
// use the default category filter condition.
if (filterdata) {
@@ -180,7 +203,7 @@ export const init = (
};
// Add listeners for the sorting, paging and clear actions.
document.addEventListener('click', e => {
document.querySelector(SELECTORS.QUESTION_BANK_WINDOW).addEventListener('click', e => {
const sortableLink = e.target.closest(SELECTORS.SORT_LINK);
const paginationLink = e.target.closest(SELECTORS.PAGINATION_LINK);
const clearLink = e.target.closest(Selectors.filterset.actions.resetFilters);
@@ -30,6 +30,7 @@ $string['categorymoved'] = 'The category has been moved successfully.';
$string['confirmdelete'] = 'Delete category "{$a}"?. Any child categories will be moved to this category\'s parent.';
$string['descriptionnotdisplayed'] = 'Categories description not displayed';
$string['displaydescription'] = 'Categories description displayed';
$string['error:category'] = 'You must select a valid category';
$string['idnumberexists'] = 'ID number already in use. Change it to move or update category.';
$string['lastcategoryinthiscontext'] = 'This category is the only one in this context. You need to add another category if this category is to be moved.';
$string['movecategory'] = 'Move {$a}';
@@ -132,3 +132,20 @@ Feature: A teacher can put questions in categories in the question bank
And I open the "last" add to quiz menu
And I follow "from question bank"
And the field "Also show questions from subcategories" matches value "0"
Scenario: Filter question by an invalid category should show validation error
When I am on the "Course 1" "core_question > course question bank" page
And I click on "Default for C1" "text" in the ".form-autocomplete-selection" "css_element"
And I click on "Apply filters" "button"
Then the "Category" field validity check should return "false"
And the "Category" field validation message should contain "You must select a valid category"
Scenario: Correcting an invalid category should no longer show validation error
When I am on the "Course 1" "core_question > course question bank" page
# First try to submit with a blank category.
And I click on "Default for C1" "text" in the ".form-autocomplete-selection" "css_element"
And I click on "Apply filters" "button"
# Then apply a correct category.
And I apply question bank filter "Category" with value "Used category"
Then the "Category" field validity check should return "true"
And I should see "Test question to be moved"