MDL-75985 behat: Standardise HTML output when comparing editor content

This commit adds a standardise_html function and updates the matches
function to compare normalised content.

This allows for a wider variety of valid editor output to be handled
using the standard value matching steps in Behat, thus supporting
editors other than Atto better.
This commit is contained in:
Andrew Nicols 2022-10-13 16:03:49 +08:00
parent 3bc792b9b8
commit febd5d944c
3 changed files with 72 additions and 6 deletions

View file

@ -93,7 +93,60 @@ class behat_form_editor extends behat_form_textarea {
* @return bool The provided value matches the field value?
*/
public function matches($expectedvalue) {
// A text editor may silently wrap the content in p tags (or not). Neither is an error.
return $this->text_matches($expectedvalue) || $this->text_matches('<p>' . $expectedvalue . '</p>');
// Fetch the actual value to save fetching it multiple times.
$actualvalue = $this->get_value();
if ($this->text_matches($expectedvalue, $actualvalue)) {
// The text is an exact match already.
return true;
}
if ($this->text_matches("<p>{$expectedvalue}</p>", $actualvalue)) {
// A text editor may silently wrap the content in p tags.
return true;
}
// Standardise both the expected value and the actual field value.
// We are likely dealing with HTML content, given this is an editor.
$expectedvalue = $this->standardise_html($expectedvalue);
$actualvalue = $this->standardise_html($actualvalue);
// Note: We don't need to worry about the floats here that we care about in text_matches.
// That condition isn't relevant to the content of an editor.
if ($expectedvalue === $actualvalue) {
return true;
}
return false;
}
/**
* Standardises the HTML content for comparison.
*
* @param string $html The HTML content to standardise
* @return string The standardised HTML content
*/
protected function standardise_html(string $html): string {
$document = new DOMDocument();
$errorstate = libxml_use_internal_errors(true);
// Format the whitespace nicely.
$document->preserveWhiteSpace = false;
$document->formatOutput = true;
// Wrap the content in a DIV element so that it is not parsed weirdly.
// Note: We must remove newlines too because DOMDocument does not do so, despite preserveWhiteSpace being false.
// Unfortunately this is slightly limited in that it will also remove newlines from <pre> content and similar.
$document->loadHTML(str_replace("\n", "", "<div>{$html}</div>"), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$document->normalizeDocument();
libxml_clear_errors();
libxml_use_internal_errors($errorstate);
// Save the content of the 'div' element, removing the <div> and </div> tags at the start and end.
return trim(substr(
$document->saveHTML($document->getElementsByTagName('div')->item(0)),
5,
-6
));
}
}

View file

@ -244,18 +244,21 @@ class behat_form_field implements behat_session_interface {
* Checks if the provided text matches the field value.
*
* @param string $expectedvalue
* @param string|null $actualvalue The actual value. If not specified, this will be fetched from $this->get_value().
* @return bool
*/
protected function text_matches($expectedvalue) {
protected function text_matches($expectedvalue, ?string $actualvalue = null): bool {
$actualvalue = $actualvalue ?? $this->get_value();
// Non strict string comparison.
if (trim($expectedvalue) == trim($this->get_value())) {
if (trim($expectedvalue) == trim($actualvalue)) {
return true;
}
// Do one more matching attempt for floats that are valid with current decsep in use
// (let's continue non strict comparing them as strings, but once unformatted).
$expectedfloat = unformat_float(trim($expectedvalue), true);
$actualfloat = unformat_float(trim($this->get_value()), true);
$actualfloat = unformat_float(trim($actualvalue), true);
// If they aren't null or false, then we are good to be compared (basically is_numeric()).
$goodfloats = !is_null($expectedfloat) && ($expectedfloat !== false) &&
!is_null($actualfloat) && ($actualfloat !== false);

View file

@ -1776,7 +1776,17 @@ EOF;
* @param string $keys The key, or list of keys, to type
*/
public function i_type(string $keys): void {
behat_base::type_keys($this->getSession(), str_split($keys));
// Certain keys, such as the newline character, must be converted to the appropriate character code.
// Without this, keys will behave differently depending on the browser.
$keylist = array_map(function($key): string {
switch ($key) {
case '\n':
behat_keys::ENTER;
default:
return $key;
}
}, str_split($keys));
behat_base::type_keys($this->getSession(), $keylist);
}
/**