MDL-84595 tool_behat: Allow accessibility tests to run for a single Node

This commit is contained in:
Andrew Nicols
2025-02-20 22:28:24 +08:00
parent 5e7cde3baf
commit beb5ba99e7
3 changed files with 82 additions and 20 deletions
+15
View File
@@ -1676,4 +1676,19 @@ EOF;
return $result ?: null;
}
/**
* Prepare an xpath for insertion into Selenium JavaScript.
*
* @param string $xpath
* @return string
*/
protected function prepare_xpath_for_javascript(string $xpath): string {
$newlines = [
"\r\n",
"\r",
"\n",
];
return str_replace($newlines, ' ', $xpath);
}
}
+61 -13
View File
@@ -14,16 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Steps definitions to open and close action menus.
*
* @package core
* @category test
* @copyright 2020 Andrew Nicols <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use Behat\Mink\Exception\{DriverException, ExpectationException};
use Behat\Mink\Exception\DriverException;
use Behat\Mink\Exception\ExpectationException;
use Behat\Mink\Element\NodeElement;
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
@@ -38,7 +31,6 @@ require_once(__DIR__ . '/../../behat/behat_base.php');
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_accessibility extends behat_base {
/**
* Run the axe-core accessibility tests.
*
@@ -62,6 +54,37 @@ class behat_accessibility extends behat_base {
);
}
/**
* Run the axe-core accessibility tests for a page region.
*
* There are standard tags to ensure WCAG 2.1 A, WCAG 2.1 AA, and Section 508 compliance.
* It is also possible to specify any desired optional tags.
*
* See {@link https://github.com/dequelabs/axe-core/blob/v4.10.0/doc/rule-descriptions.md} for the list of available tags
*
* @Then the :element :selector should meet accessibility standards
* @Then the :element :selector should meet accessibility standards with :extratags extra tests
* @Then the :element :selector should meet :standardtags accessibility standards
* @param string $element The element to run the tests on
* @param string $selector The selector to use to find the element
* @param string $standardtags Comma-separated list of standard tags to run
* @param string $extratags Comma-separated list of tags to run in addition to the standard tags
*/
public function run_axe_validation_for_tags_within_element(
string $element,
string $selector,
string $standardtags = '',
string $extratags = '',
): void {
$node = $this->get_selected_node($selector, $element);
$this->run_axe_for_tags(
// Turn the comma-separated string into an array of trimmed values, filtering out empty values.
array_filter(array_map('trim', explode(',', $standardtags))),
array_filter(array_map('trim', explode(',', $extratags))),
$node,
);
}
/**
* Run the Axe tests.
*
@@ -70,8 +93,13 @@ class behat_accessibility extends behat_base {
*
* @param array $standardtags The list of standard tags to run
* @param array $extratags The list of tags, in addition to the standard tags, to run
* @param null|NodeElement $containerelement The element to run the tests on
*/
protected function run_axe_for_tags(array $standardtags = [], array $extratags = []): void {
protected function run_axe_for_tags(
array $standardtags = [],
array $extratags = [],
?NodeElement $containerelement = null,
): void {
if (!behat_config_manager::get_behat_run_config_value('axe')) {
return;
}
@@ -86,13 +114,33 @@ class behat_accessibility extends behat_base {
$axeurl = (new \moodle_url('/lib/behat/axe/axe.min.js'))->out(false);
$axeconfig = $this->get_axe_config_for_tags($standardtags, $extratags);
$xpath = '';
if ($containerelement) {
$xpath = $this->prepare_xpath_for_javascript($containerelement->getXpath());
}
$runaxe = <<<EOF
(axeurl => {
const runTests = () => {
const axeTag = document.querySelector('script[data-purpose="axe"]');
axeTag.dataset.results = null;
axe.run({$axeconfig})
const getRun = () => {
const xpath = "{$xpath}";
if (xpath.length) {
const targetElements = [];
const results = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);
let targetElement = results.iterateNext();
while (targetElement) {
targetElements.push(targetElement);
targetElement = results.iterateNext();
}
return axe.run(targetElements, {$axeconfig});
}
return axe.run({$axeconfig});
};
getRun()
.then(results => {
axeTag.dataset.results = JSON.stringify({
violations: results.violations,
+6 -7
View File
@@ -977,13 +977,12 @@ class behat_general extends behat_base {
list($preselector, $prelocator) = $this->transform_selector($preselectortype, $preelement);
list($postselector, $postlocator) = $this->transform_selector($postselectortype, $postelement);
$newlines = [
"\r\n",
"\r",
"\n",
];
$prexpath = str_replace($newlines, ' ', $this->find($preselector, $prelocator, false, $containernode)->getXpath());
$postxpath = str_replace($newlines, ' ', $this->find($postselector, $postlocator, false, $containernode)->getXpath());
$prexpath = $this->prepare_xpath_for_javascript(
$this->find($preselector, $prelocator, false, $containernode)->getXpath()
);
$postxpath = $this->prepare_xpath_for_javascript(
$this->find($postselector, $postlocator, false, $containernode)->getXpath()
);
if ($this->running_javascript()) {
// The xpath to do this was running really slowly on certain Chrome versions so we are using