mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 00:12:56 +02:00
MDL-55091 phpunit: Add support for findNodes, as they are now deprecated
https://github.com/sebastianbergmann/phpunit/issues/1292
This commit is contained in:
parent
ef1d90480d
commit
165b70a16a
@ -37,6 +37,10 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class base_testcase extends PHPUnit_Framework_TestCase {
|
||||
// @codingStandardsIgnoreStart
|
||||
// Following code is legacy code from phpunit to support assertTag
|
||||
// and assertNotTag.
|
||||
|
||||
/**
|
||||
* Note: we are overriding this method to remove the deprecated error
|
||||
* @see https://tracker.moodle.org/browse/MDL-47129
|
||||
@ -50,7 +54,7 @@ abstract class base_testcase extends PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
public static function assertTag($matcher, $actual, $message = '', $ishtml = true) {
|
||||
$dom = PHPUnit_Util_XML::load($actual, $ishtml);
|
||||
$tags = PHPUnit_Util_XML::findNodes($dom, $matcher, $ishtml);
|
||||
$tags = self::findNodes($dom, $matcher, $ishtml);
|
||||
$matched = count($tags) > 0 && $tags[0] instanceof DOMNode;
|
||||
self::assertTrue($matched, $message);
|
||||
}
|
||||
@ -68,8 +72,503 @@ abstract class base_testcase extends PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
public static function assertNotTag($matcher, $actual, $message = '', $ishtml = true) {
|
||||
$dom = PHPUnit_Util_XML::load($actual, $ishtml);
|
||||
$tags = PHPUnit_Util_XML::findNodes($dom, $matcher, $ishtml);
|
||||
$tags = self::findNodes($dom, $matcher, $ishtml);
|
||||
$matched = count($tags) > 0 && $tags[0] instanceof DOMNode;
|
||||
self::assertFalse($matched, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate list of keys in the associative array.
|
||||
*
|
||||
* @param array $hash
|
||||
* @param array $validKeys
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws PHPUnit_Framework_Exception
|
||||
*/
|
||||
public static function assertValidKeys(array $hash, array $validKeys) {
|
||||
$valids = array();
|
||||
|
||||
// Normalize validation keys so that we can use both indexed and
|
||||
// associative arrays.
|
||||
foreach ($validKeys as $key => $val) {
|
||||
is_int($key) ? $valids[$val] = null : $valids[$key] = $val;
|
||||
}
|
||||
|
||||
$validKeys = array_keys($valids);
|
||||
|
||||
// Check for invalid keys.
|
||||
foreach ($hash as $key => $value) {
|
||||
if (!in_array($key, $validKeys)) {
|
||||
$unknown[] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($unknown)) {
|
||||
throw new PHPUnit_Framework_Exception(
|
||||
'Unknown key(s): ' . implode(', ', $unknown)
|
||||
);
|
||||
}
|
||||
|
||||
// Add default values for any valid keys that are empty.
|
||||
foreach ($valids as $key => $value) {
|
||||
if (!isset($hash[$key])) {
|
||||
$hash[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse out the options from the tag using DOM object tree.
|
||||
*
|
||||
* @param DOMDocument $dom
|
||||
* @param array $options
|
||||
* @param bool $isHtml
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function findNodes(DOMDocument $dom, array $options, $isHtml = true) {
|
||||
$valid = array(
|
||||
'id', 'class', 'tag', 'content', 'attributes', 'parent',
|
||||
'child', 'ancestor', 'descendant', 'children', 'adjacent-sibling'
|
||||
);
|
||||
|
||||
$filtered = array();
|
||||
$options = self::assertValidKeys($options, $valid);
|
||||
|
||||
// find the element by id
|
||||
if ($options['id']) {
|
||||
$options['attributes']['id'] = $options['id'];
|
||||
}
|
||||
|
||||
if ($options['class']) {
|
||||
$options['attributes']['class'] = $options['class'];
|
||||
}
|
||||
|
||||
$nodes = array();
|
||||
|
||||
// find the element by a tag type
|
||||
if ($options['tag']) {
|
||||
if ($isHtml) {
|
||||
$elements = self::getElementsByCaseInsensitiveTagName(
|
||||
$dom,
|
||||
$options['tag']
|
||||
);
|
||||
} else {
|
||||
$elements = $dom->getElementsByTagName($options['tag']);
|
||||
}
|
||||
|
||||
foreach ($elements as $element) {
|
||||
$nodes[] = $element;
|
||||
}
|
||||
|
||||
if (empty($nodes)) {
|
||||
return false;
|
||||
}
|
||||
} // no tag selected, get them all
|
||||
else {
|
||||
$tags = array(
|
||||
'a', 'abbr', 'acronym', 'address', 'area', 'b', 'base', 'bdo',
|
||||
'big', 'blockquote', 'body', 'br', 'button', 'caption', 'cite',
|
||||
'code', 'col', 'colgroup', 'dd', 'del', 'div', 'dfn', 'dl',
|
||||
'dt', 'em', 'fieldset', 'form', 'frame', 'frameset', 'h1', 'h2',
|
||||
'h3', 'h4', 'h5', 'h6', 'head', 'hr', 'html', 'i', 'iframe',
|
||||
'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'link',
|
||||
'map', 'meta', 'noframes', 'noscript', 'object', 'ol', 'optgroup',
|
||||
'option', 'p', 'param', 'pre', 'q', 'samp', 'script', 'select',
|
||||
'small', 'span', 'strong', 'style', 'sub', 'sup', 'table',
|
||||
'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'title',
|
||||
'tr', 'tt', 'ul', 'var',
|
||||
// HTML5
|
||||
'article', 'aside', 'audio', 'bdi', 'canvas', 'command',
|
||||
'datalist', 'details', 'dialog', 'embed', 'figure', 'figcaption',
|
||||
'footer', 'header', 'hgroup', 'keygen', 'mark', 'meter', 'nav',
|
||||
'output', 'progress', 'ruby', 'rt', 'rp', 'track', 'section',
|
||||
'source', 'summary', 'time', 'video', 'wbr'
|
||||
);
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
if ($isHtml) {
|
||||
$elements = self::getElementsByCaseInsensitiveTagName(
|
||||
$dom,
|
||||
$tag
|
||||
);
|
||||
} else {
|
||||
$elements = $dom->getElementsByTagName($tag);
|
||||
}
|
||||
|
||||
foreach ($elements as $element) {
|
||||
$nodes[] = $element;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($nodes)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// filter by attributes
|
||||
if ($options['attributes']) {
|
||||
foreach ($nodes as $node) {
|
||||
$invalid = false;
|
||||
|
||||
foreach ($options['attributes'] as $name => $value) {
|
||||
// match by regexp if like "regexp:/foo/i"
|
||||
if (preg_match('/^regexp\s*:\s*(.*)/i', $value, $matches)) {
|
||||
if (!preg_match($matches[1], $node->getAttribute($name))) {
|
||||
$invalid = true;
|
||||
}
|
||||
} // class can match only a part
|
||||
elseif ($name == 'class') {
|
||||
// split to individual classes
|
||||
$findClasses = explode(
|
||||
' ',
|
||||
preg_replace("/\s+/", ' ', $value)
|
||||
);
|
||||
|
||||
$allClasses = explode(
|
||||
' ',
|
||||
preg_replace("/\s+/", ' ', $node->getAttribute($name))
|
||||
);
|
||||
|
||||
// make sure each class given is in the actual node
|
||||
foreach ($findClasses as $findClass) {
|
||||
if (!in_array($findClass, $allClasses)) {
|
||||
$invalid = true;
|
||||
}
|
||||
}
|
||||
} // match by exact string
|
||||
else {
|
||||
if ($node->getAttribute($name) != $value) {
|
||||
$invalid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if every attribute given matched
|
||||
if (!$invalid) {
|
||||
$filtered[] = $node;
|
||||
}
|
||||
}
|
||||
|
||||
$nodes = $filtered;
|
||||
$filtered = array();
|
||||
|
||||
if (empty($nodes)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// filter by content
|
||||
if ($options['content'] !== null) {
|
||||
foreach ($nodes as $node) {
|
||||
$invalid = false;
|
||||
|
||||
// match by regexp if like "regexp:/foo/i"
|
||||
if (preg_match('/^regexp\s*:\s*(.*)/i', $options['content'], $matches)) {
|
||||
if (!preg_match($matches[1], self::getNodeText($node))) {
|
||||
$invalid = true;
|
||||
}
|
||||
} // match empty string
|
||||
elseif ($options['content'] === '') {
|
||||
if (self::getNodeText($node) !== '') {
|
||||
$invalid = true;
|
||||
}
|
||||
} // match by exact string
|
||||
elseif (strstr(self::getNodeText($node), $options['content']) === false) {
|
||||
$invalid = true;
|
||||
}
|
||||
|
||||
if (!$invalid) {
|
||||
$filtered[] = $node;
|
||||
}
|
||||
}
|
||||
|
||||
$nodes = $filtered;
|
||||
$filtered = array();
|
||||
|
||||
if (empty($nodes)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// filter by parent node
|
||||
if ($options['parent']) {
|
||||
$parentNodes = self::findNodes($dom, $options['parent'], $isHtml);
|
||||
$parentNode = isset($parentNodes[0]) ? $parentNodes[0] : null;
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
if ($parentNode !== $node->parentNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filtered[] = $node;
|
||||
}
|
||||
|
||||
$nodes = $filtered;
|
||||
$filtered = array();
|
||||
|
||||
if (empty($nodes)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// filter by child node
|
||||
if ($options['child']) {
|
||||
$childNodes = self::findNodes($dom, $options['child'], $isHtml);
|
||||
$childNodes = !empty($childNodes) ? $childNodes : array();
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
foreach ($node->childNodes as $child) {
|
||||
foreach ($childNodes as $childNode) {
|
||||
if ($childNode === $child) {
|
||||
$filtered[] = $node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$nodes = $filtered;
|
||||
$filtered = array();
|
||||
|
||||
if (empty($nodes)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// filter by adjacent-sibling
|
||||
if ($options['adjacent-sibling']) {
|
||||
$adjacentSiblingNodes = self::findNodes($dom, $options['adjacent-sibling'], $isHtml);
|
||||
$adjacentSiblingNodes = !empty($adjacentSiblingNodes) ? $adjacentSiblingNodes : array();
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
$sibling = $node;
|
||||
|
||||
while ($sibling = $sibling->nextSibling) {
|
||||
if ($sibling->nodeType !== XML_ELEMENT_NODE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($adjacentSiblingNodes as $adjacentSiblingNode) {
|
||||
if ($sibling === $adjacentSiblingNode) {
|
||||
$filtered[] = $node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$nodes = $filtered;
|
||||
$filtered = array();
|
||||
|
||||
if (empty($nodes)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// filter by ancestor
|
||||
if ($options['ancestor']) {
|
||||
$ancestorNodes = self::findNodes($dom, $options['ancestor'], $isHtml);
|
||||
$ancestorNode = isset($ancestorNodes[0]) ? $ancestorNodes[0] : null;
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
$parent = $node->parentNode;
|
||||
|
||||
while ($parent && $parent->nodeType != XML_HTML_DOCUMENT_NODE) {
|
||||
if ($parent === $ancestorNode) {
|
||||
$filtered[] = $node;
|
||||
}
|
||||
|
||||
$parent = $parent->parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
$nodes = $filtered;
|
||||
$filtered = array();
|
||||
|
||||
if (empty($nodes)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// filter by descendant
|
||||
if ($options['descendant']) {
|
||||
$descendantNodes = self::findNodes($dom, $options['descendant'], $isHtml);
|
||||
$descendantNodes = !empty($descendantNodes) ? $descendantNodes : array();
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
foreach (self::getDescendants($node) as $descendant) {
|
||||
foreach ($descendantNodes as $descendantNode) {
|
||||
if ($descendantNode === $descendant) {
|
||||
$filtered[] = $node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$nodes = $filtered;
|
||||
$filtered = array();
|
||||
|
||||
if (empty($nodes)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// filter by children
|
||||
if ($options['children']) {
|
||||
$validChild = array('count', 'greater_than', 'less_than', 'only');
|
||||
$childOptions = self::assertValidKeys(
|
||||
$options['children'],
|
||||
$validChild
|
||||
);
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
$childNodes = $node->childNodes;
|
||||
|
||||
foreach ($childNodes as $childNode) {
|
||||
if ($childNode->nodeType !== XML_CDATA_SECTION_NODE &&
|
||||
$childNode->nodeType !== XML_TEXT_NODE) {
|
||||
$children[] = $childNode;
|
||||
}
|
||||
}
|
||||
|
||||
// we must have children to pass this filter
|
||||
if (!empty($children)) {
|
||||
// exact count of children
|
||||
if ($childOptions['count'] !== null) {
|
||||
if (count($children) !== $childOptions['count']) {
|
||||
break;
|
||||
}
|
||||
} // range count of children
|
||||
elseif ($childOptions['less_than'] !== null &&
|
||||
$childOptions['greater_than'] !== null) {
|
||||
if (count($children) >= $childOptions['less_than'] ||
|
||||
count($children) <= $childOptions['greater_than']) {
|
||||
break;
|
||||
}
|
||||
} // less than a given count
|
||||
elseif ($childOptions['less_than'] !== null) {
|
||||
if (count($children) >= $childOptions['less_than']) {
|
||||
break;
|
||||
}
|
||||
} // more than a given count
|
||||
elseif ($childOptions['greater_than'] !== null) {
|
||||
if (count($children) <= $childOptions['greater_than']) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// match each child against a specific tag
|
||||
if ($childOptions['only']) {
|
||||
$onlyNodes = self::findNodes(
|
||||
$dom,
|
||||
$childOptions['only'],
|
||||
$isHtml
|
||||
);
|
||||
|
||||
// try to match each child to one of the 'only' nodes
|
||||
foreach ($children as $child) {
|
||||
$matched = false;
|
||||
|
||||
foreach ($onlyNodes as $onlyNode) {
|
||||
if ($onlyNode === $child) {
|
||||
$matched = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$matched) {
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$filtered[] = $node;
|
||||
}
|
||||
}
|
||||
|
||||
$nodes = $filtered;
|
||||
|
||||
if (empty($nodes)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// return the first node that matches all criteria
|
||||
return !empty($nodes) ? $nodes : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively get flat array of all descendants of this node.
|
||||
*
|
||||
* @param DOMNode $node
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function getDescendants(DOMNode $node) {
|
||||
$allChildren = array();
|
||||
$childNodes = $node->childNodes ? $node->childNodes : array();
|
||||
|
||||
foreach ($childNodes as $child) {
|
||||
if ($child->nodeType === XML_CDATA_SECTION_NODE ||
|
||||
$child->nodeType === XML_TEXT_NODE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$children = self::getDescendants($child);
|
||||
$allChildren = array_merge($allChildren, $children, array($child));
|
||||
}
|
||||
|
||||
return isset($allChildren) ? $allChildren : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets elements by case insensitive tagname.
|
||||
*
|
||||
* @param DOMDocument $dom
|
||||
* @param string $tag
|
||||
*
|
||||
* @return DOMNodeList
|
||||
*/
|
||||
protected static function getElementsByCaseInsensitiveTagName(DOMDocument $dom, $tag) {
|
||||
$elements = $dom->getElementsByTagName(strtolower($tag));
|
||||
|
||||
if ($elements->length == 0) {
|
||||
$elements = $dom->getElementsByTagName(strtoupper($tag));
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text value of this node's child text node.
|
||||
*
|
||||
* @param DOMNode $node
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getNodeText(DOMNode $node) {
|
||||
if (!$node->childNodes instanceof DOMNodeList) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$result = '';
|
||||
|
||||
foreach ($node->childNodes as $childNode) {
|
||||
if ($childNode->nodeType === XML_TEXT_NODE ||
|
||||
$childNode->nodeType === XML_CDATA_SECTION_NODE) {
|
||||
$result .= trim($childNode->data) . ' ';
|
||||
} else {
|
||||
$result .= self::getNodeText($childNode);
|
||||
}
|
||||
}
|
||||
|
||||
return str_replace(' ', ' ', $result);
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreEnd
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user