Continuing with tests

This commit is contained in:
Dustin Wilson 2021-11-18 16:55:41 -06:00
parent c03aebc52a
commit 82c9dac054
9 changed files with 138 additions and 56 deletions

View file

@ -5,7 +5,7 @@
"require": { "require": {
"php": ">=8.0", "php": ">=8.0",
"ext-dom": "*", "ext-dom": "*",
"mensbeam/html-parser": "dev-pretty-print", "mensbeam/html-parser": "dev-master",
"mensbeam/framework": "dev-main", "mensbeam/framework": "dev-main",
"symfony/css-selector": "^5.3" "symfony/css-selector": "^5.3"
}, },

9
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "80f59b5505ffc9b8d7fd2a408538d861", "content-hash": "6ed06e9556bdcc4bbbb5f3f23958e33d",
"packages": [ "packages": [
{ {
"name": "mensbeam/framework", "name": "mensbeam/framework",
@ -59,11 +59,11 @@
}, },
{ {
"name": "mensbeam/html-parser", "name": "mensbeam/html-parser",
"version": "dev-pretty-print", "version": "dev-master",
"source": { "source": {
"type": "git", "type": "git",
"url": "mensbeam-gitea:MensBeam/HTML-Parser.git", "url": "mensbeam-gitea:MensBeam/HTML-Parser.git",
"reference": "8361ea0d88c4406213e39e2cb983bfcb1e9b9b37" "reference": "5495e7c81e36409bebb998962f8624b5744a1b30"
}, },
"require": { "require": {
"ext-dom": "*", "ext-dom": "*",
@ -77,6 +77,7 @@
"suggest": { "suggest": {
"ext-ctype": "Improved performance" "ext-ctype": "Improved performance"
}, },
"default-branch": true,
"type": "library", "type": "library",
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@ -129,7 +130,7 @@
"parsing", "parsing",
"whatwg" "whatwg"
], ],
"time": "2021-11-17T05:59:36+00:00" "time": "2021-11-18T17:39:20+00:00"
}, },
{ {
"name": "mensbeam/intl", "name": "mensbeam/intl",

View file

@ -438,41 +438,7 @@ class Document extends Node {
$node = $node->innerNode; $node = $node->innerNode;
} }
$parserConfig = new ParserConfig(); return Serializer::serialize($node, $config);
foreach ($config as $key => $value) {
switch ($key) {
case 'indentStep':
if (!is_int($value)) {
$type = gettype($value);
if ($type === 'object') {
$type = get_class($value);
}
trigger_error("Value for serializer configuration option \"$key\" must be an integer; $type given", \E_USER_WARNING);
continue 2;
}
break;
case 'indentWithSpaces':
case 'reformatWhitespace':
case 'serializeBooleanAttributeValues':
case 'serializeForeignVoidEndTags':
if (!is_bool($value)) {
$type = gettype($value);
if ($type === 'object') {
$type = get_class($value);
}
trigger_error("Value for serializer configuration option \"$key\" must be an integer; $type given", \E_USER_WARNING);
continue 2;
}
break;
default:
trigger_error("\"$key\" is an invalid serializer configuration option", \E_USER_WARNING);
continue 2;
}
$parserConfig->$key = $value;
}
return Serializer::serialize($node, $parserConfig);
} }

View file

@ -64,6 +64,21 @@ class Element extends Node {
} }
public function getAttribute(string $qualifiedName): ?string {
# The getAttribute(qualifiedName) method steps are:
#
# 1. Let attr be the result of getting an attribute given qualifiedName and this.
$attr = $this->getAttributeNode($qualifiedName);
# 2. If attr is null, return null.
if ($attr === null) {
return null;
}
# 3. Return attrs value.
// Uncoerce the value if necessary
$value = $attr->value;
return (!strpos($value, 'U')) ? $value : $this->uncoerceName($value);
}
public function getAttributeNames(): array { public function getAttributeNames(): array {
# The getAttributeNames() method steps are to return the qualified names of the # The getAttributeNames() method steps are to return the qualified names of the
# attributes in thiss attribute list, in order; otherwise a new list. # attributes in thiss attribute list, in order; otherwise a new list.
@ -78,8 +93,40 @@ class Element extends Node {
return $list; return $list;
} }
public function getAttributeNS(string $namespace, string $localName): ?string { public function getAttributeNodeNS(?string $namespace, string $localName): ?Attr {
return $this->innerNode->getAttributeNS($namespace, $localName); # The getAttributeNodeNS(namespace, localName) method steps are to return the
# result of getting an attribute given namespace, localName, and this.
#
# To get an attribute by namespace and local name given a namespace, localName,
# and element element, run these steps:
#
# 1. If namespace is the empty string, then set it to null.
if ($namespace === '') {
$namespace = null;
}
# 2. Return the attribute in elements attribute list whose namespace is namespace
# and local name is localName, if any; otherwise null.
// Going to try to handle this by getting the PHP DOM to do the heavy lifting
// when we can because it's faster.
$value = $this->innerNode->getAttributeNodeNS($namespace, $localName);
if (!$value) {
// Replace any offending characters with "UHHHHHH" where H are the uppercase
// hexadecimal digits of the character's code point
$localName = $this->coerceName($localName);
// The PHP DOM does not acknowledge the presence of XMLNS-namespace attributes
// sometimes, too... so this will get those as well in those circumstances.
$attributes = $this->innerNode->attributes;
foreach ($attributes as $a) {
if ($a->namespaceURI === $namespace && $a->localName === $localName) {
return $this->innerNode->ownerDocument->getWrapperNode($a);
}
}
return null;
}
return ($value !== false) ? $this->innerNode->ownerDocument->getWrapperNode($value) : null;
} }
public function getAttributeNode(string $qualifiedName): ?Attr { public function getAttributeNode(string $qualifiedName): ?Attr {
@ -118,6 +165,23 @@ class Element extends Node {
return ($attr !== false) ? $this->innerNode->ownerDocument->getWrapperNode($attr) : null; return ($attr !== false) ? $this->innerNode->ownerDocument->getWrapperNode($attr) : null;
} }
public function getAttributeNS(?string $namespace, string $localName): ?string {
# The getAttributeNS(namespace, localName) method steps are:
#
# 1. Let attr be the result of getting an attribute given namespace, localName,
# and this.
$attr = $this->getAttributeNodeNS($namespace, $localName);
# 2. If attr is null, return null.
if ($attr === null) {
return null;
}
# 3. Return attrs value.
// Uncoerce the value if necessary
return $attr->value;
}
public function hasAttributes(): bool { public function hasAttributes(): bool {
# The hasAttributes() method steps are to return false if thiss attribute list # The hasAttributes() method steps are to return false if thiss attribute list
# is empty; otherwise true. # is empty; otherwise true.

View file

@ -48,10 +48,6 @@ class Document extends \DOMDocument {
} }
public function getInnerNode(WrapperNode $node): ?\DOMNode {
return Reflection::getProtectedProperty($node, 'innerNode');
}
public function getWrapperNode(\DOMNode $node): ?WrapperNode { public function getWrapperNode(\DOMNode $node): ?WrapperNode {
// If the node is a Document then the wrapperNode is this's wrapperNode // If the node is a Document then the wrapperNode is this's wrapperNode
// property. // property.

View file

@ -16,18 +16,11 @@ use MensBeam\HTML\Parser\{
class Serializer extends ParserSerializer { class Serializer extends ParserSerializer {
protected static function getTemplateContent(\DOMElement $node, ?Config $config = null): \DOMNode { protected static function getTemplateContent(\DOMElement $node): \DOMNode {
// NOTE: PHP's DOM does not support the content property on template elements
// natively. This method exists purely so implementors of userland PHP DOM
// solutions may extend this method to get template contents how they need them.
return Reflection::getProtectedProperty($node->ownerDocument->getWrapperNode($node)->content, 'innerNode'); return Reflection::getProtectedProperty($node->ownerDocument->getWrapperNode($node)->content, 'innerNode');
} }
protected static function isPreformattedContent(\DOMNode $node): bool { protected static function isPreformattedContent(\DOMNode $node): bool {
// NOTE: This method is used only when pretty printing. Implementors of userland
// PHP DOM solutions with template contents will need to extend this method to
// be able to moonwalk through document fragment hosts.
$n = $node; $n = $node;
do { do {
if ($n instanceof \DOMElement) { if ($n instanceof \DOMElement) {

View file

@ -0,0 +1,63 @@
<?php
/**
* @license MIT
* Copyright 2017 Dustin Wilson, J. King, et al.
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
namespace MensBeam\HTML\DOM\TestCase;
use MensBeam\HTML\DOM\{
Document,
DOMException,
Element,
Node,
Text,
XMLDocument
};
/** @covers \MensBeam\HTML\DOM\Element */
class TestElement extends \PHPUnit\Framework\TestCase {
/**
* @covers \MensBeam\HTML\DOM\Element::getAttributeNames
*
* @covers \MensBeam\HTML\DOM\Collection::__construct
* @covers \MensBeam\HTML\DOM\Collection::item
* @covers \MensBeam\HTML\DOM\Document::__construct
* @covers \MensBeam\HTML\DOM\Document::load
* @covers \MensBeam\HTML\DOM\DocumentOrElement::getElementsByTagName
* @covers \MensBeam\HTML\DOM\DOMImplementation::__construct
* @covers \MensBeam\HTML\DOM\Element::__construct
* @covers \MensBeam\HTML\DOM\HTMLCollection::item
* @covers \MensBeam\HTML\DOM\HTMLCollection::offsetGet
* @covers \MensBeam\HTML\DOM\Node::__construct
* @covers \MensBeam\HTML\DOM\Node::getInnerDocument
* @covers \MensBeam\HTML\DOM\Node::hasChildNodes
* @covers \MensBeam\HTML\DOM\Inner\Document::__construct
* @covers \MensBeam\HTML\DOM\Inner\Document::getWrapperNode
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::get
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::has
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::key
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::set
* @covers \MensBeam\HTML\DOM\Inner\Reflection::createFromProtectedConstructor
*/
public function testMethod_getAttributeNames() {
$d = new Document('<!DOCTYPE html><html><body><div id="ook" class="eek" ack="ack" foo="foo" bar="bar"></div></body></html>');
$div = $d->getElementsByTagName('div')[0];
$this->assertSame([ 'id', 'class', 'ack', 'foo', 'bar' ], $div->getAttributeNames());
}
public function testMethod_getAttributeNS() {
$d = new Document('<!DOCTYPE html><html><head></head><body><svg xmlns="' . Node::SVG_NAMESPACE . '" xmlns:xlink="' . Node::XLINK_NAMESPACE . '"></svg></body></html>');
$svg = $d->getElementsByTagNameNS(Node::SVG_NAMESPACE, 'svg')[0];
// Parser doesn't parse xmlns prefixed attributes except xlink, so let's add one manually instead to test coercion.
$svg->setAttributeNS(Node::XMLNS_NAMESPACE, 'xmlns:poop💩', 'https://poop💩.poop');
$this->assertSame(Node::SVG_NAMESPACE, $svg->getAttributeNS(Node::XMLNS_NAMESPACE, 'xmlns'));
$this->assertSame(Node::XLINK_NAMESPACE, $svg->getAttributeNS(Node::XMLNS_NAMESPACE, 'xlink'));
$this->assertSame('https://poop💩.poop', $svg->getAttributeNS(Node::XMLNS_NAMESPACE, 'poop💩'));
}
}

View file

@ -289,7 +289,6 @@ class TestNode extends \PHPUnit\Framework\TestCase {
* @covers \MensBeam\HTML\DOM\Text::__construct * @covers \MensBeam\HTML\DOM\Text::__construct
* @covers \MensBeam\HTML\DOM\Inner\Document::__construct * @covers \MensBeam\HTML\DOM\Inner\Document::__construct
* @covers \MensBeam\HTML\DOM\Inner\Document::__get_wrapperNode * @covers \MensBeam\HTML\DOM\Inner\Document::__get_wrapperNode
* @covers \MensBeam\HTML\DOM\Inner\Document::getInnerNode
* @covers \MensBeam\HTML\DOM\Inner\Document::getWrapperNode * @covers \MensBeam\HTML\DOM\Inner\Document::getWrapperNode
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::get * @covers \MensBeam\HTML\DOM\Inner\NodeCache::get
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::has * @covers \MensBeam\HTML\DOM\Inner\NodeCache::has
@ -864,7 +863,6 @@ class TestNode extends \PHPUnit\Framework\TestCase {
* @covers \MensBeam\HTML\DOM\Text::__construct * @covers \MensBeam\HTML\DOM\Text::__construct
* @covers \MensBeam\HTML\DOM\Inner\Document::__construct * @covers \MensBeam\HTML\DOM\Inner\Document::__construct
* @covers \MensBeam\HTML\DOM\Inner\Document::__get_wrapperNode * @covers \MensBeam\HTML\DOM\Inner\Document::__get_wrapperNode
* @covers \MensBeam\HTML\DOM\Inner\Document::getInnerNode
* @covers \MensBeam\HTML\DOM\Inner\Document::getWrapperNode * @covers \MensBeam\HTML\DOM\Inner\Document::getWrapperNode
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::get * @covers \MensBeam\HTML\DOM\Inner\NodeCache::get
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::has * @covers \MensBeam\HTML\DOM\Inner\NodeCache::has

View file

@ -18,6 +18,7 @@
<testsuite name="DOM"> <testsuite name="DOM">
<file>cases/TestDocument.php</file> <file>cases/TestDocument.php</file>
<file>cases/TestDocumentOrElement.php</file> <file>cases/TestDocumentOrElement.php</file>
<file>cases/TestElement.php</file>
<file>cases/TestNode.php</file> <file>cases/TestNode.php</file>
</testsuite> </testsuite>
</testsuites> </testsuites>