diff --git a/.gitignore b/.gitignore index 93cd4d7..fa1ca68 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,8 @@ manual node_modules /test*.html /test*.php -lib/old +old +tests/cases/old # General *.DS_Store diff --git a/lib/Attr.php b/lib/Attr.php index ccd0d10..8ac15ea 100644 --- a/lib/Attr.php +++ b/lib/Attr.php @@ -27,7 +27,8 @@ class Attr extends Node { protected function __get_ownerElement(): Element { // PHP's DOM does this correctly already. - return $this->innerNode->ownerDocument->getWrapperNode($this->innerNode->ownerElement); + $wrapperNode = &$this->innerNode->ownerDocument->getWrapperNode($this->innerNode->ownerElement); + return $wrapperNode; } protected function __get_prefix(): string { diff --git a/lib/CDATASection.php b/lib/CDATASection.php index 64269f8..5ff93f4 100644 --- a/lib/CDATASection.php +++ b/lib/CDATASection.php @@ -9,4 +9,8 @@ declare(strict_types=1); namespace MensBeam\HTML\DOM; -class CDATASection extends Text {} \ No newline at end of file +class CDATASection extends Text { + public function __construct(string $data = '') { + $this->innerNode = new \DOMCDATASection($data); + } +} \ No newline at end of file diff --git a/lib/ChildNode.php b/lib/ChildNode.php index fa505e0..8199b82 100644 --- a/lib/ChildNode.php +++ b/lib/ChildNode.php @@ -7,8 +7,10 @@ declare(strict_types=1); namespace MensBeam\HTML\DOM; -use MensBeam\Framework\MagicProperties, - MensBeam\HTML\DOM\InnerNode\Reflection; +use MensBeam\HTML\DOM\InnerNode\{ + Document as InnerDocument, + Reflection +}; trait ChildNode { @@ -23,28 +25,18 @@ trait ChildNode { */ public function moonwalk(?\Closure $filter = null, bool $includeReferenceNode = false): \Generator { $node = $this->parentNode; + if ($node !== null) { + $node = Reflection::getProtectedProperty($node, 'innerNode'); + $doc = (!$node instanceof InnerDocument) ? $node->ownerDocument : $node; + do { $next = $node->parentNode; - $result = ($filter === null) ? true : $filter($node); - // Have to do type checking here because PHP is lacking in advanced typing - if ($result !== true && $result !== false && $result !== null) { - $type = gettype($result); - if ($type === 'object') { - $type = get_class($result); - } - throw new Exception(Exception::RETURN_TYPE_ERROR, 'Closure', '?bool', $type); - } + $wrapperNode = $doc->getWrapperNode($node); + $result = ($filter === null) ? true : $filter($wrapperNode); if ($result === true) { - yield $node; - } - - if ($node instanceof DocumentFragment) { - $host = Reflection::getProtectedProperty($node, 'host'); - if ($host !== null) { - $next = $host->get(); - } + yield $wrapperNode; } } while ($node = $next); } @@ -59,22 +51,23 @@ trait ChildNode { * the iteration. */ public function walkFollowing(?\Closure $filter = null, bool $includeReferenceNode = false): \Generator { - $node = ($includeReferenceNode) ? $this : $this->nextSibling; + $node = null; + if ($includeReferenceNode) { + $node = $this->innerNode; + } elseif ($this->nextSibling !== null) { + $node = Reflection::getProtectedProperty($this->nextSibling, 'innerNode'); + } + if ($node !== null) { + $doc = (!$node instanceof InnerDocument) ? $node->ownerDocument : $node; + do { $next = $node->nextSibling; - $result = ($filter === null) ? true : $filter($node); - // Have to do type checking here because PHP is lacking in advanced typing - if ($result !== true && $result !== false && $result !== null) { - $type = gettype($result); - if ($type === 'object') { - $type = get_class($result); - } - throw new Exception(Exception::RETURN_TYPE_ERROR, 'Closure', '?bool', $type); - } + $wrapperNode = $doc->getWrapperNode($node); + $result = ($filter === null) ? true : $filter($wrapperNode); if ($result === true) { - yield $node; + yield $wrapperNode; } } while ($node = $next); } @@ -89,22 +82,23 @@ trait ChildNode { * the iteration. */ public function walkPreceding(?\Closure $filter = null, bool $includeReferenceNode = false): \Generator { - $node = ($includeReferenceNode) ? $this : $this->previousSibling; + $node = null; + if ($includeReferenceNode) { + $node = $this->innerNode; + } elseif ($this->nextSibling !== null) { + $node = Reflection::getProtectedProperty($this->previousSibling, 'innerNode'); + } + if ($node !== null) { + $doc = (!$node instanceof InnerDocument) ? $node->ownerDocument : $node; + do { $next = $node->previousSibling; - $result = ($filter === null) ? true : $filter($node); - // Have to do type checking here because PHP is lacking in advanced typing - if ($result !== true && $result !== false && $result !== null) { - $type = gettype($result); - if ($type === 'object') { - $type = get_class($result); - } - throw new Exception(Exception::RETURN_TYPE_ERROR, 'Closure', '?bool', $type); - } + $wrapperNode = $doc->getWrapperNode($node); + $result = ($filter === null) ? true : $filter($wrapperNode); if ($result === true) { - yield $node; + yield $wrapperNode; } } while ($node = $next); } diff --git a/lib/Document.php b/lib/Document.php index c41a829..b05d45c 100644 --- a/lib/Document.php +++ b/lib/Document.php @@ -20,10 +20,28 @@ class Document extends Node { protected string $_contentType = 'text/html'; protected DOMImplementation $_implementation; + protected function __get_body(): Element { + if ($this->documentElement === null || !$this->documentElement->hasChildNodes()) { + return null; + } + + # The body element of a document is the first of the html element's children + # that is either a body element or a frameset element, or null if there is no + # such element. + return $this->documentElement->firstChild->walkFollowing(function($n) { + $name = strtolower($n->nodeName); + return ($n instanceof Element && $n->namespaceURI === Parser::HTML_NAMESPACE && ($name === 'body' || $name === 'frameset')); + }, true)->current(); + } + protected function __get_contentType(): string { return $this->_contentType; } + protected function __get_documentElement(): ?Element { + return $this->innerNode->getWrapperNode($this->innerNode->documentElement); + } + protected function __get_implementation(): DOMImplementation { return $this->_implementation; } diff --git a/lib/Element.php b/lib/Element.php index 56cbdd2..0b25728 100644 --- a/lib/Element.php +++ b/lib/Element.php @@ -11,7 +11,7 @@ use MensBeam\HTML\Parser; class Element extends Node { - use ParentNode; + use ChildNode, ParentNode; protected function __get_namespaceURI(): string { // PHP's DOM uses null incorrectly for the HTML namespace, and if you attempt to diff --git a/lib/Exception.php b/lib/Exception.php index 262b3b4..299d810 100644 --- a/lib/Exception.php +++ b/lib/Exception.php @@ -10,7 +10,7 @@ namespace MensBeam\HTML\DOM; use MensBeam\Framework\Exception as FrameworkException; -class DOMException extends FrameworkException { +class Exception extends FrameworkException { public const CLIENT_ONLY_NOT_IMPLEMENTED = 301; diff --git a/lib/InnerNode/Document.php b/lib/InnerNode/Document.php index 8634d1f..5d04edb 100644 --- a/lib/InnerNode/Document.php +++ b/lib/InnerNode/Document.php @@ -42,10 +42,13 @@ class Document extends \DOMDocument { } - public function getWrapperNode(?\DOMNode $node = null): WrapperNode { + public function getWrapperNode(?\DOMNode $node = null): ?WrapperNode { + if ($node === null) { + return null; + } // If the node is a Document then the wrapperNode is this's wrapperNode // property. - if ($node instanceof Document || $node === null) { + if ($node instanceof Document) { return $this->wrapperNode; } @@ -91,7 +94,21 @@ class Document extends \DOMDocument { $className = 'XMLDocument'; } - // Nodes cannot be created from their constructors normally - return Reflection::createFromProtectedConstructor(self::$parentNamespace . "\\$className", $node); + // If the class is to be a CDATASection, DocumentFragment, or Text then the + // object needs to be created differently because they have public constructors, + // unlike other nodes. + if ($className === 'CDATASection' || $className === 'DocumentFragment' || $className === 'Text') { + $reflector = new \ReflectionClass(self::$parentNamespace . "\\$className"); + $wrapperNode = $reflector->newInstanceWithoutConstructor(); + $property = new \ReflectionProperty($wrapperNode, 'innerNode'); + $property->setAccessible(true); + $property->setValue($wrapperNode, $node); + return $wrapperNode; + } else { + $wrapperNode = Reflection::createFromProtectedConstructor(self::$parentNamespace . "\\$className", $node); + } + + $this->nodeMap->set($wrapperNode, $this); + return $wrapperNode; } } diff --git a/lib/Node.php b/lib/Node.php index 4efed1c..43d6d50 100644 --- a/lib/Node.php +++ b/lib/Node.php @@ -99,7 +99,8 @@ abstract class Node { protected function __get_firstChild(): ?Node { // PHP's DOM does this correctly already. - return $this->innerNode->firstChild; + $doc = ($this instanceof Document) ? $this->innerNode : $this->innerNode->ownerDocument; + return $doc->getWrapperNode($this->innerNode->firstChild); } protected function __get_isConnected(): bool { @@ -111,17 +112,17 @@ abstract class Node { protected function __get_lastChild(): ?Node { // PHP's DOM does this correctly already. - return $this->innerNode->lastChild; + return $this->innerNode->ownerDocument->getWrapperNode($this->innerNode->lastChild); } protected function __get_previousSibling(): ?Node { // PHP's DOM does this correctly already. - return $this->innerNode->previousSibling; + return $this->innerNode->ownerDocument->getWrapperNode($this->innerNode->previousSibling); } protected function __get_nextSibling(): ?Node { // PHP's DOM does this correctly already. - return $this->innerNode->nextSibling; + return $this->innerNode->ownerDocument->getWrapperNode($this->innerNode->nextSibling); } protected function __get_nodeName(): string { @@ -183,7 +184,7 @@ abstract class Node { return null; } - return $this->innerNode->ownerDocument->getWrapperNode(); + return $this->innerNode->ownerDocument->getWrapperNode($this->innerNode->ownerDocument); } protected function __get_parentElement(): ?Element { @@ -862,11 +863,10 @@ abstract class Node { } } - $childNodes = $this->childNodes; - foreach ($childNodes as $c) { - if ($c instanceof Element) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } + if ($this->firstChild !== null && $this->firstChild->walkFollowing(function($n) { + return ($n instanceof Element); + }, true)->current() !== null) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); } } @@ -874,11 +874,10 @@ abstract class Node { # parent has a doctype child, child is non-null and an element is preceding # child, or child is null and parent has an element child. elseif ($node instanceof DocumentType) { - $childNodes = $this->childNodes; - foreach ($childNodes as $c) { - if ($c instanceof DocumentType) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } + if ($this->firstChild !== null && $this->firstChild->walkFollowing(function($n) { + return ($n instanceof DocumentType); + }, true)->current() !== null) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); } if ($child !== null) { @@ -889,11 +888,10 @@ abstract class Node { } } } else { - $childNodes = $this->childNodes; - foreach ($childNodes as $c) { - if ($c instanceof Element) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } + if ($this->firstChild !== null && $this->firstChild->walkFollowing(function($n) { + return ($n instanceof Element); + }, true)->current() !== null) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); } } } diff --git a/lib/ParentNode.php b/lib/ParentNode.php index 0ab07a2..b94df58 100644 --- a/lib/ParentNode.php +++ b/lib/ParentNode.php @@ -7,7 +7,10 @@ declare(strict_types=1); namespace MensBeam\HTML\DOM; -use MensBeam\Framework\MagicProperties; +use MensBeam\HTML\DOM\InnerNode\{ + Document as InnerDocument, + Reflection +}; trait ParentNode { @@ -21,35 +24,29 @@ trait ParentNode { * the iteration. */ public function walk(?\Closure $filter = null, bool $includeReferenceNode = false): \Generator { - $node = ($includeReferenceNode && !$this instanceof DocumentFragment) ? $this : $this->firstChild; + $node = null; + if ($includeReferenceNode && !$this instanceof DocumentFragment) { + $node = $this->innerNode; + } elseif ($this->firstChild !== null) { + $node = Reflection::getProtectedProperty($this->firstChild, 'innerNode'); + } if ($node !== null) { + $doc = (!$node instanceof InnerDocument) ? $node->ownerDocument : $node; + do { $next = $node->nextSibling; - $result = ($filter === null) ? true : $filter($node); - // Have to do type checking here because PHP is lacking in advanced typing - if ($result !== true && $result !== false && $result !== null) { - $type = gettype($result); - if ($type === 'object') { - $type = get_class($result); - } - throw new Exception(Exception::RETURN_TYPE_ERROR, 'Closure', '?bool', $type); - } + $wrapperNode = $doc->getWrapperNode($node); + $result = ($filter === null) ? true : $filter($wrapperNode); if ($result === true) { - yield $node; + yield $wrapperNode; } // If the filter returns true (accept) or false (skip) and the node wasn't // removed in the filter iterate through the children - if ($result !== null && $node->parentNode !== null) { - if ($node instanceof HTMLTemplateElement) { - $node = $node->content; - } - - if ($node->hasChildNodes()) { - yield from $node->walk($filter); - } + if ($result !== null && $node->parentNode !== null && $node->hasChildNodes()) { + yield from $node->walk($filter); } } while ($node = $next); } diff --git a/tests/cases/TestChildNode.php b/tests/cases/TestChildNode.php deleted file mode 100644 index 8135897..0000000 --- a/tests/cases/TestChildNode.php +++ /dev/null @@ -1,239 +0,0 @@ -appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $div = $d->body->appendChild($d->createElement('div')); - $o = $d->body->appendChild($d->createTextNode('ook')); - $div2 = $d->body->appendChild($d->createElement('div')); - - // On node with parent - $div->after($d->createElement('span'), $o, 'eek'); - $this->assertSame('
ookeek
', (string)$d->body); - $div->after($o); - $this->assertSame('
ookeek
', (string)$d->body); - - // On node with no parent - $c = $d->createComment('ook'); - $this->assertNull($c->after($d->createTextNode('ook'))); - - // On node with parent - $br = $d->body->insertBefore($d->createElement('br'), $div); - $e = $d->createTextNode('eek'); - $div->before($d->createElement('span'), $o, 'eek', $e, $br); - $this->assertSame('ookeekeek
eek
', (string)$d->body); - $div->before($o); - $this->assertSame('eekeek
ook
eek
', (string)$d->body); - - // On node with no parent - $c = $d->createComment('ook'); - $this->assertNull($c->before($d->createTextNode('ook'))); - - // On node with parent - $s = $d->createElement('span'); - $br->replaceWith('ack', $o, $e, $s); - $this->assertSame('eekackookeek
eek
', (string)$d->body); - $s->replaceWith($o); - $this->assertSame('eekackeekook
eek
', (string)$d->body); - - // On node with no parent - $c = $d->createComment('ook'); - $this->assertNull($c->replaceWith($d->createTextNode('ook'))); - - // Parent within node - $o->replaceWith('poo', $o, $e); - $this->assertSame('eekackpooookeek
eek
', (string)$d->body); - } - - - public function provideAfterBeforeReplaceWithFailures(): array { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $div = $d->body->appendChild($d->createElement('div')); - - return [ - [ function() use($div) { - $div->after(false); - } ], - [ function() use($div) { - $div->before(false); - } ], - [ function() use($div) { - $div->replaceWith(false); - } ], - [ function() use($div) { - $div->after(new \DateTime); - } ], - [ function() use($div) { - $div->before(new \DateTime); - } ], - [ function() use($div) { - $div->replaceWith(new \DateTime); - } ] - ]; - } - - /** - * @dataProvider provideAfterBeforeReplaceWithFailures - * @covers \MensBeam\HTML\DOM\ChildNode::after - * @covers \MensBeam\HTML\DOM\ChildNode::before - * @covers \MensBeam\HTML\DOM\ChildNode::replaceWith - */ - public function testAfterBeforeReplaceWithFailures(\Closure $closure): void { - $this->expectException(Exception::class); - $this->expectExceptionCode(Exception::ARGUMENT_TYPE_ERROR); - $closure(); - } - - - /** @covers \MensBeam\HTML\DOM\ChildNode::moonwalk */ - public function testMoonwalk(): void { - // Test removal of elements when moonwalking - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $div = $d->body->appendChild($d->createElement('div')); - $div = $div->appendChild($d->createElement('div')); - $div = $div->appendChild($d->createElement('div')); - $div->setAttribute('class', 'delete-me'); - $div = $div->appendChild($d->createElement('div')); - $t = $div->appendChild($d->createTextNode('ook')); - - $divs = $t->moonwalk(function($n) { - return ($n instanceof Element && $n->nodeName === 'div'); - }); - - foreach ($divs as $div) { - if ($div->getAttribute('class') === 'delete-me') { - $div->parentNode->removeChild($div); - } - } - - $this->assertSame('
', (string)$d->body); - - // Test moonwalking through template barriers - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $t = $d->body->appendChild($d->createElement('template')); - - $w = $t->content->appendChild($d->createTextNode('ook'))->moonwalk(); - $this->assertTrue($t->content->isSameNode($w->current())); - $w->next(); - $this->assertTrue($t->isSameNode($w->current())); - } - - public function provideMoonwalkFailures(): iterable { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $d->body->innerHTML = '

Ook

Eek

Ook eek, ook?

'; - $main = $d->body->getElementsByTagName('main')->item(0); - - return [ - [ function() use ($main) { - $main->moonwalk(function($n) { - return 'ook'; - })->current(); - } ], - [ function() use ($main) { - $main->moonwalk(function($n) { - return new \DateTime(); - })->current(); - } ] - ]; - } - - /** - * @dataProvider provideMoonwalkFailures - * @covers \MensBeam\HTML\DOM\DOMException::__construct - * @covers \MensBeam\HTML\DOM\ChildNode::moonwalk - */ - public function testMoonwalkFailures(\Closure $closure): void { - $this->expectException(Exception::class); - $this->expectExceptionCode(Exception::CLOSURE_RETURN_TYPE_ERROR); - $closure(); - } - - - public function provideWalkFailures(): iterable { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $d->body->innerHTML = '

Ook

Eek

Ook eek, ook?

'; - - return [ - [ function() use ($d) { - $d->body->firstChild->walkFollowing(function($n) { - return 'ook'; - })->current(); - } ], - [ function() use ($d) { - $d->body->firstChild->walkFollowing(function($n) { - return new \DateTime(); - })->current(); - } ], - [ function() use ($d) { - $d->body->lastChild->walkPreceding(function($n) { - return 'ook'; - })->current(); - } ], - [ function() use ($d) { - $d->body->lastChild->walkPreceding(function($n) { - return new \DateTime(); - })->current(); - } ] - ]; - } - - /** - * @dataProvider provideWalkFailures - * @covers \MensBeam\HTML\DOM\DOMException::__construct - * @covers \MensBeam\HTML\DOM\ChildNode::walkFollowing - * @covers \MensBeam\HTML\DOM\ChildNode::walkPreceding - */ - public function testWalkFailures(\Closure $closure): void { - $this->expectException(Exception::class); - $this->expectExceptionCode(Exception::CLOSURE_RETURN_TYPE_ERROR); - $closure(); - } - - - /** @covers \MensBeam\HTML\DOM\ParentNode::walk */ - public function testWalkPreceding(): void { - // Test removal of elements when walking - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $d->body->innerHTML = '

Ook

Eek

Ook eek, ook?

'; - - $this->assertNotNull($d->body->lastChild->walkPreceding(function($n) { - return ($n instanceof Element && $n->nodeName === 'main'); - }, true)->current()); - } -} \ No newline at end of file diff --git a/tests/cases/TestDocument.php b/tests/cases/TestDocument.php deleted file mode 100644 index 901939c..0000000 --- a/tests/cases/TestDocument.php +++ /dev/null @@ -1,532 +0,0 @@ -createElement('template'); - - $d2 = new Document(); - $t2 = $d2->adoptNode($t, true); - $this->assertSame($d2, $t2->ownerDocument); - $this->assertNull($t->parentNode); - - /*$d = new \DOMDocument(); - $t = $d->createElement('template'); - // Add a child template to cover recursive template conversions. - $t->appendChild($d->createElement('template')); - $this->assertSame(\DOMElement::class, $t::class); - - $d2 = new Document(); - $t2 = $d2->importNode($t, true); - $this->assertSame(HTMLTemplateElement::class, $t2::class);*/ - } - - public function provideAttributeNodeCreation(): iterable { - return [ - [ 'test', 'test' ], - [ 'TEST', 'test' ], - [ 'test:test', 'testU00003Atest' ], - [ 'TEST:TEST', 'testU00003Atest' ] - ]; - } - - /** - * @dataProvider provideAttributeNodeCreation - * @covers \MensBeam\HTML\DOM\Document::createAttribute - */ - public function testAttributeNodeCreation(string $nameIn, string $local): void { - // Test without a document element and with - $d = new Document(); - $a = $d->createAttribute($nameIn); - $this->assertSame($local, $a->localName); - - $d = new Document(); - $d->appendChild($d->createElement('html')); - $a = $d->createAttribute($nameIn); - $this->assertSame($local, $a->localName); - } - - - /** - * @covers \MensBeam\HTML\DOM\Document::createAttribute - * @covers \MensBeam\HTML\DOM\DOMException::__construct - */ - public function testAttributeNodeCreationFailure(): void { - $this->expectException(DOMException::class); - $this->expectExceptionCode(DOMException::INVALID_CHARACTER); - $d = new Document(); - $d->createAttribute(''); - } - - - public function provideAttributeNodeNSCreation(): iterable { - return [ - [ 'fake_ns', 'test', 'fake_ns', '', 'test' ], - [ 'fake_ns', 'test:test', 'fake_ns', 'test', 'test' ], - [ 'fake_ns', 'TEST:TEST', 'fake_ns', 'TEST', 'TEST' ], - [ 'another_fake_ns', 'steaming💩:poop💩', 'another_fake_ns', 'steamingU01F4A9', 'poopU01F4A9' ], - // An empty string for a prefix is technically incorrect, but we cannot fix that. - [ '', 'poop💩', null, '', 'poopU01F4A9' ], - // An empty string for a prefix is technically incorrect, but we cannot fix that. - [ null, 'poop💩', null, '', 'poopU01F4A9' ] - ]; - } - - /** - * @dataProvider provideAttributeNodeNSCreation - * @covers \MensBeam\HTML\DOM\Document::createAttributeNS - * @covers \MensBeam\HTML\DOM\Document::validateAndExtract - */ - public function testAttributeNodeNSCreation(?string $nsIn, string $nameIn, ?string $nsExpected, ?string $prefixExpected, string $localNameExpected): void { - // Test without a document element and with - $d = new Document(); - $a = $d->createAttributeNS($nsIn, $nameIn); - $this->assertSame($nsExpected, $a->namespaceURI); - $this->assertSame($prefixExpected, $a->prefix); - $this->assertSame($localNameExpected, $a->localName); - - $d = new Document(); - $d->appendChild($d->createElement('html')); - $a = $d->createAttributeNS($nsIn, $nameIn); - $this->assertSame($nsExpected, $a->namespaceURI); - $this->assertSame($prefixExpected, $a->prefix); - $this->assertSame($localNameExpected, $a->localName); - } - - - public function provideDisabledMethods(): iterable { - return [ - [ 'createCDATASection', 'ook' ], - [ 'createEntityReference', 'ook' ], - [ 'loadXML', 'ook' ], - [ 'registerNodeClass', 'ook', null ], - [ 'relaxNGValidate', 'ook' ], - [ 'relaxNGValidateSource', 'ook' ], - [ 'saveXML', null ], - [ 'schemaValidate', 'ook', 0 ], - [ 'schemaValidateSource', 'ook', 0 ], - [ 'validate', null ], - [ 'xinclude', null ], - ]; - } - - /** - * @dataProvider provideDisabledMethods - * @covers \MensBeam\HTML\DOM\Document::createEntityReference - * @covers \MensBeam\HTML\DOM\Document::loadXML - * @covers \MensBeam\HTML\DOM\Document::saveXML - * @covers \MensBeam\HTML\DOM\Document::validate - * @covers \MensBeam\HTML\DOM\Document::xinclude - * @covers \MensBeam\HTML\DOM\DOMException::__construct - */ - public function testDisabledMethods(string $methodName, ...$arguments): void { - $this->expectException(Exception::class); - $this->expectExceptionCode(Exception::DISABLED_METHOD); - $d = new Document(); - $d->$methodName(...$arguments); - } - - - /** - * @covers \MensBeam\HTML\DOM\Document::__construct - * @covers \MensBeam\HTML\DOM\Document::convertTemplate - * @covers \MensBeam\HTML\DOM\Document::load - * @covers \MensBeam\HTML\DOM\Document::loadDOM - * @covers \MensBeam\HTML\DOM\Document::loadHTML - * @covers \MensBeam\HTML\DOM\Document::loadHTMLFile - * @covers \MensBeam\HTML\DOM\Document::preInsertionValidity - * @covers \MensBeam\HTML\DOM\Document::replaceTemplates - * @covers \MensBeam\HTML\DOM\Document::__get_compatMode - * @covers \MensBeam\HTML\DOM\Document::__get_URL - * @covers \MensBeam\HTML\DOM\NodeTrait::getRootNode - */ - public function testDocumentCreation(): void { - // Test null source - $d = new Document(); - $this->assertSame('MensBeam\HTML\DOM\Document', get_class($d)); - $this->assertSame(null, $d->firstChild); - - // Test compatibility mode - $d = new Document('Ook!'); - $this->assertSame('BackCompat', $d->compatMode); - - // Test DOM source - $d = new \DOMDocument(); - $d->appendChild($d->createElement('html')); - $d2 = new Document(); - $d2->appendChild($d2->createElement('html')); - $d2->loadDOM($d); - $d3 = new Document($d); - $this->assertSame('MensBeam\HTML\DOM\Element', get_class($d3->firstChild)); - $this->assertSame('html', $d3->firstChild->nodeName); - - // Test file source - $vfs = vfsStream::setup('DOM', 0777, [ 'test.html' => << - - - - Ook - - - HTML ]); - $f = $vfs->url() . '/test.html'; - - // Test nonexistent file source - $d = new Document(); - $this->assertFalse(@$d->load('fileDoesNotExist.html')); - $d->load($f); - $this->assertNotNull($d->documentElement); - $this->assertSame('ISO-2022-JP', $d->charset); - - // Test http source - $d = new Document(); - $d->load('https://google.com'); - $this->assertNotNull($d->documentElement); - $this->assertSame('UTF-8', $d->charset); - $this->assertSame('https://google.com', $d->URL); - $this->assertNull($d->documentURI); - - // Test document encoding - $d = new Document(); - $d->loadHTMLFile($f, null, 'UTF-8'); - $this->assertSame('UTF-8', $d->charset); - - // Test real document loading - $d = new Document(); - $d->loadHTMLFile(dirname(__FILE__) . '/../test.html', null, 'UTF-8'); - $this->assertStringStartsWith('file://', $d->URL); - - // Test templates in source - $d = new Document(''); - $t = $d->getElementsByTagName('template')->item(0); - $this->assertSame(HTMLTemplateElement::class, get_class($t)); - $this->assertSame(HTMLTemplateElement::class, get_class($t->content->firstChild)); - } - - - public function provideElementCreation(): iterable { - return [ - // HTML element - [ 'div', 'div', Element::class ], - // HTML element and uppercase qualified name - [ 'DIV', 'div', Element::class ], - // Template element - [ 'template', 'template', HTMLTemplateElement::class ], - // Template element and uppercase qualified name - [ 'TEMPLATE', 'template', HTMLTemplateElement::class ], - // Name coercion - [ 'poop💩', 'poopU01F4A9', Element::class ] - ]; - } - - /** - * @dataProvider provideElementCreation - * @covers \MensBeam\HTML\DOM\Document::createElement - */ - public function testElementCreation(string $nameIn, string $nameExpected, string $classExpected): void { - $d = new Document; - $n = $d->createElement($nameIn); - $this->assertInstanceOf($classExpected, $n); - $this->assertNotNull($n->ownerDocument); - $this->assertSame($nameExpected, $n->nodeName); - } - - - public function provideElementCreationFailures(): iterable { - return [ - [ function() { - $d = new Document(); - $d->createElement('ook', 'FAIL'); - }, DOMException::NOT_SUPPORTED ], - [ function() { - $d = new Document(); - $d->createElement(''); - }, DOMException::INVALID_CHARACTER ] - ]; - } - - - /** - * @dataProvider provideElementCreationFailures - * @covers \MensBeam\HTML\DOM\Document::__construct - * @covers \MensBeam\HTML\DOM\DOMException::__construct - */ - public function testElementCreationFailures(\Closure $closure, int $errorCode): void { - $this->expectException(DOMException::class); - $this->expectExceptionCode($errorCode); - $closure(); - } - - - public function provideElementCreationNS(): iterable { - return [ - // HTML element with a null namespace - [ null, null, 'div', 'div', Element::class ], - // Template element with a null namespace - [ null, null, 'template', 'template', HTMLTemplateElement::class ], - // Template element with a null namespace and uppercase name - [ null, null, 'TEMPLATE', 'TEMPLATE', HTMLTemplateElement::class ], - // Template element - [ Parser::HTML_NAMESPACE, Parser::HTML_NAMESPACE, 'template', 'template', HTMLTemplateElement::class ], - // SVG element with SVG namespace - [ Parser::SVG_NAMESPACE, Parser::SVG_NAMESPACE, 'svg', 'svg', Element::class ], - // SVG element with SVG namespace and uppercase local name - [ Parser::SVG_NAMESPACE, Parser::SVG_NAMESPACE, 'SVG', 'SVG', Element::class ], - // Name coercion - [ 'steaming💩', 'steaming💩', 'poop💩', 'poopU01F4A9', Element::class ] - ]; - } - - /** - * @dataProvider provideElementCreationNS - * @covers \MensBeam\HTML\DOM\Document::createElementNS - * @covers \MensBeam\HTML\DOM\Document::validateAndExtract - */ - public function testElementCreationNS(?string $nsIn, ?string $nsExpected, string $localNameIn, string $localNameExpected, string $classExpected): void { - $d = new Document(); - $n = $d->createElementNS($nsIn, $localNameIn); - $this->assertInstanceOf($classExpected, $n); - $this->assertNotNull($n->ownerDocument); - $this->assertSame($nsExpected, $n->namespaceURI); - $this->assertSame($localNameExpected, $n->localName); - } - - - public function provideElementCreationNSFailures(): iterable { - return [ - [ function() { - $d = new Document(); - $d->createElementNS('ook', 'ook', 'FAIL'); - }, DOMException::NOT_SUPPORTED ], - [ function() { - $d = new Document(); - $d->createElementNS(null, ''); - }, DOMException::INVALID_CHARACTER ], - [ function() { - $d = new Document(); - $d->createElementNS(null, 'xmlns'); - }, DOMException::NAMESPACE_ERROR ] - ]; - } - - /** - * @dataProvider provideElementCreationNSFailures - * @covers \MensBeam\HTML\DOM\Document::createElementNS - * @covers \MensBeam\HTML\DOM\Document::validateAndExtract - * @covers \MensBeam\HTML\DOM\DOMException::__construct - */ - public function testElementCreationNSFailures(\Closure $closure, int $errorCode): void { - $this->expectException(DOMException::class); - $this->expectExceptionCode($errorCode); - $closure(); - } - - - /** - * @covers \MensBeam\HTML\DOM\Document::save - * @covers \MensBeam\HTML\DOM\Document::saveHTMLFile - */ - public function testFileSaving(): void { - $vfs = vfsStream::setup('DOM', 0777); - $path = $vfs->url() . '/test.html'; - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->save($path); - $this->assertSame('', file_get_contents($path)); - $d->saveHTMLFile($path); - $this->assertSame('', file_get_contents($path)); - } - - - /** @covers \MensBeam\HTML\DOM\Document::importNode */ - public function testImportNode() { - $d = new Document(); - $t = $d->createElement('template'); - - $d2 = new Document(); - $t2 = $d2->importNode($t, true); - $this->assertFalse($t2->ownerDocument->isSameNode($t->ownerDocument)); - $this->assertSame($t2::class, $t::class); - - $d = new \DOMDocument(); - $t = $d->createElement('template'); - // Add a child template to cover recursive template conversions. - $t->appendChild($d->createElement('template')); - $this->assertSame(\DOMElement::class, $t::class); - - $d2 = new Document(); - $t2 = $d2->importNode($t, true); - $this->assertSame(HTMLTemplateElement::class, $t2::class); - } - - - /** @covers \MensBeam\HTML\DOM\Document::importNode */ - public function testImportingNodesFailure() { - $this->expectException(DOMException::class); - $this->expectExceptionCode(DOMException::NOT_SUPPORTED); - $d = new \DOMDocument(); - $c = $d->createCDATASection('fail'); - $d2 = new Document(); - $d2->importNode($c); - } - - - /** @covers \MensBeam\HTML\DOM\Document::__get_body */ - public function testPropertyGetBody(): void { - $d = new Document(); - $this->assertNull($d->body); - $d->appendChild($d->createElement('html')); - $this->assertNull($d->body); - $d->documentElement->appendChild($d->createTextNode(' ')); - $this->assertNull($d->body); - $f = $d->createElement('frameset'); - $d->documentElement->appendChild($f); - $this->assertNotNull($d->body); - $d->documentElement->removeChild($f); - } - - - /** @covers \MensBeam\HTML\DOM\Document::__set_body */ - public function testPropertySetBody(): void { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $b = $d->createElement('body'); - $d->body = $b; - $this->assertSame('body', $d->body->nodeName); - $d->body = $b; - $this->assertSame('body', $d->body->nodeName); - - $b = $d->createElement('body'); - $b->appendChild($d->createTextNode('Ook')); - $d->body = $b; - $this->assertSame('Ook', $d->body->firstChild->data); - } - - public function providePropertySetBodyFailures(): iterable { - $result = []; - $d = new Document(); - $result[] = [ $d, $d->createElement('body') ]; - $d = new Document(); - $result[] = [ $d, $d->createElement('div') ]; - $d = new Document(); - $result[] = [ $d, $d->createTextNode('FAIL') ]; - return $result; - } - - /** - * @dataProvider providePropertySetBodyFailures - * @covers \MensBeam\HTML\DOM\Document::__set_body - * @covers \MensBeam\HTML\DOM\DOMException::__construct - */ - public function testPropertySetBodyFailures(Document $document, \DOMNode $node): void { - $this->expectException(DOMException::class); - $this->expectExceptionCode(DOMException::HIERARCHY_REQUEST_ERROR); - $document->body = $node; - } - - - /** - * @covers \MensBeam\HTML\DOM\Document::__get_charset - * @covers \MensBeam\HTML\DOM\Document::__get_characterSet - * @covers \MensBeam\HTML\DOM\Document::__get_inputEncoding - */ - public function testPropertyGetCharset(): void { - $d = new Document(null, 'UTF-8'); - $this->assertSame('UTF-8', $d->charset); - $this->assertSame('UTF-8', $d->characterSet); - $this->assertSame('UTF-8', $d->inputEncoding); - - $d = new Document(''); - $this->assertSame('gb18030', $d->charset); - $this->assertSame('gb18030', $d->characterSet); - $this->assertSame('gb18030', $d->inputEncoding); - } - - - public function providePropertyGetCompatMode(): iterable { - return [ - // Empty document - [ null, 'CSS1Compat' ], - // Document without doctype - [ '', 'BackCompat' ], - // Document with doctype - [ '', 'CSS1Compat' ] - ]; - } - - /** - * @dataProvider providePropertyGetCompatMode - * @covers \MensBeam\HTML\DOM\Document::__get_compatMode - */ - public function testPropertyGetCompatMode(?string $html, string $compatMode): void { - $d = new Document($html); - $this->assertSame($compatMode, $d->compatMode); - } - - - /** @covers \MensBeam\HTML\DOM\Document::__get_contentType */ - public function testPropertyGetContentType(): void { - $d = new Document(); - $this->assertSame('text/html', $d->contentType); - } - - - /** @covers \MensBeam\HTML\DOM\Document::__get_xpath */ - public function testPropertyGetXPath(): void { - $d = new Document(); - $this->assertSame('DOMXPath', get_class($d->xpath)); - } - - - /** - * @covers \MensBeam\HTML\DOM\Document::__destruct - * @covers \MensBeam\HTML\DOM\ElementMap::add - * @covers \MensBeam\HTML\DOM\ElementMap::delete - * @covers \MensBeam\HTML\DOM\ElementMap::destroy - * @covers \MensBeam\HTML\DOM\ElementMap::has - * @covers \MensBeam\HTML\DOM\ElementMap::index - */ - public function testTemplateElementReferences(): void { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $t = $d->createElement('template'); - $this->assertFalse(ElementMap::has($t)); - $d->documentElement->appendChild($t); - $this->assertTrue(ElementMap::has($t)); - $d->__destruct(); - $this->assertFalse(ElementMap::has($t)); - - $d = new Document(); - $d->appendChild($d->createElement('html')); - - $t = $d->importNode($t); - $this->assertFalse(ElementMap::has($t)); - $d->documentElement->appendChild($t); - $this->assertTrue(ElementMap::has($t)); - $d->documentElement->removeChild($t); - $this->assertFalse(ElementMap::has($t)); - } -} \ No newline at end of file diff --git a/tests/cases/TestDocumentFragment.php b/tests/cases/TestDocumentFragment.php deleted file mode 100644 index 990d6fe..0000000 --- a/tests/cases/TestDocumentFragment.php +++ /dev/null @@ -1,68 +0,0 @@ -createDocumentFragment(); - $o = $df->appendChild($d->createElement('span')); - $o->setAttribute('id', 'eek'); - $this->assertSame(Element::class, $df->getElementById('eek')::class); - $this->assertNull($df->getElementById('ook')); - } - - /** @covers \MensBeam\HTML\DOM\DocumentFragment::__get_host */ - public function testGetHost(): void { - $d = new Document(); - // From a template - $t = $d->createElement('template'); - $this->assertSame(HTMLTemplateElement::class, get_class($t->content->host)); - // From a created document fragment - $df = $d->createDocumentFragment(); - $this->assertNull($df->host); - } - - public function provideSetHostFailures(): iterable { - return [ - [ function() { - $d = new Document(); - $t = $d->createElement('template'); - $t->content->host = $d->createElement('template'); - } ], - [ function() { - $d = new Document(); - $df = $d->createDocumentFragment(); - $df->host = $d->createElement('template'); - } ] - ]; - } - - /** - * @dataProvider provideSetHostFailures - * @covers \MensBeam\HTML\DOM\DocumentFragment::__set_host - * @covers \MensBeam\HTML\DOM\Exception::__construct - */ - public function testSetHostFailures(\Closure $closure): void { - $this->expectException(Exception::class); - $this->expectExceptionCode(Exception::READONLY_PROPERTY); - $closure(); - } -} \ No newline at end of file diff --git a/tests/cases/TestDocumentOrElement.php b/tests/cases/TestDocumentOrElement.php deleted file mode 100644 index 9905856..0000000 --- a/tests/cases/TestDocumentOrElement.php +++ /dev/null @@ -1,25 +0,0 @@ -assertSame(0, $d->getElementsByClassName('fail')->length); - $this->assertSame(0, $d->getElementsByClassName('')->length); - $d->appendChild($d->createElement('html')); - $d->documentElement->setAttribute('class', 'ook'); - $this->assertSame($d->documentElement->nodeName, $d->getElementsByClassName('ook')->item(0)->nodeName); - } -} \ No newline at end of file diff --git a/tests/cases/TestElement.php b/tests/cases/TestElement.php deleted file mode 100644 index de0c845..0000000 --- a/tests/cases/TestElement.php +++ /dev/null @@ -1,303 +0,0 @@ -createElement('html'); - $d->appendChild($e); - - $this->assertSame([], $e->getAttributeNames()); - - $e->setAttribute('ook:eek', 'ook'); - $e->setAttributeNS(Parser::XMLNS_NAMESPACE, 'xmlns:xlink', Parser::XLINK_NAMESPACE); - $e->setAttribute('ook', 'eek'); - - $this->assertSame([ - 'ook:eek', - 'xmlns:xlink', - 'ook' - ], $e->getAttributeNames()); - } - - - public function provideGetHasSetAttribute(): iterable { - return [ - [ 'ook', 'eek', 'ook', 'eek' ], - [ 'ook:eek', 'ook', 'ook:eek', 'ook' ], - [ 'poop💩', 'soccer', 'poop💩', 'soccer' ] - ]; - } - - /** - * @dataProvider provideGetHasSetAttribute - * @covers \MensBeam\HTML\DOM\Element::getAttribute - * @covers \MensBeam\HTML\DOM\Element::hasAttribute - * @covers \MensBeam\HTML\DOM\Element::setAttribute - */ - public function testGetHasSetAttribute(string $nameIn, string $valueIn, string $nameExpected, string $valueExpected): void { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $e = $d->documentElement; - $e->setAttribute($nameIn, $valueIn); - $this->assertTrue($e->hasAttribute($nameExpected)); - $this->assertSame($valueExpected, $e->getAttribute($nameExpected)); - } - - - public function provideGetHasSetAttributeNS(): iterable { - return [ - [ 'fake_ns', 'ook', 'eek', 'ookeek', 'ook', 'eek', 'ookeek' ], - [ 'another_fake_ns', 'steaming💩', 'poop💩', 'soccer', 'steaming💩', 'poop💩', 'soccer' ], - [ Parser::XMLNS_NAMESPACE, 'xmlns', 'xlink', Parser::XLINK_NAMESPACE, 'xmlns', 'xlink', Parser::XLINK_NAMESPACE ] - ]; - } - - /** - * @dataProvider provideGetHasSetAttributeNS - * @covers \MensBeam\HTML\DOM\Element::getAttributeNS - * @covers \MensBeam\HTML\DOM\Element::hasAttributeNS - * @covers \MensBeam\HTML\DOM\Element::setAttributeNS - */ - public function testGetHasSetAttributeNS(?string $namespaceIn, ?string $prefixIn, string $localNameIn, string $valueIn, ?string $prefixExpected, string $localNameExpected, string $valueExpected): void { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $e = $d->documentElement; - $qualifiedNameIn = ($prefixIn === null || $prefixIn === '') ? $localNameIn : "{$prefixIn}:{$localNameIn}"; - $e->setAttributeNS($namespaceIn, $qualifiedNameIn, $valueIn); - $this->assertTrue($e->hasAttributeNS($namespaceIn, $localNameExpected)); - $this->assertSame($valueExpected, $e->getAttributeNS($namespaceIn, $localNameExpected)); - } - - - /** - * @covers \MensBeam\HTML\DOM\Element::__get_classList - * @covers \MensBeam\HTML\DOM\TokenList::__construct - */ - public function testPropertyGetClassList(): void { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->setAttribute('class', 'ook eek ack ookeek'); - $this->assertSame(4, $d->documentElement->classList->length); - } - - - /** - * @covers \MensBeam\HTML\DOM\Element::__get_innerHTML - * @covers \MensBeam\HTML\DOM\Element::__set_innerHTML - * @covers \MensBeam\HTML\DOM\Document::importNode - */ - public function testPropertyGetSetInnerHTML(): void { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $s = $d->body->appendChild($d->createElement('span')); - $s->appendChild($d->createTextNode('ook')); - $this->assertSame('ook', $d->body->innerHTML); - - $d->body->innerHTML = '
eek
'; - $this->assertSame('
eek
', $d->body->innerHTML); - - $t = $d->body->appendChild($d->createElement('template')); - $t->innerHTML = 'ook'; - $this->assertSame('ook', $t->innerHTML); - } - - - /** - * @covers \MensBeam\HTML\DOM\Element::__get_outerHTML - * @covers \MensBeam\HTML\DOM\Element::__set_outerHTML - */ - public function testPropertyGetSetOuterHTML(): void { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $d->body->setAttribute('class', 'ook'); - $s = $d->body->appendChild($d->createElement('span')); - $s->appendChild($d->createTextNode('ook')); - $this->assertSame('ook', $d->body->outerHTML); - - $d->body->outerHTML = 'eek'; - $this->assertSame('eek', $d->body->outerHTML); - - $f = $d->createDocumentFragment(); - $div = $f->appendChild($d->createElement('div')); - $div->outerHTML = 'ook'; - $this->assertSame('ook', (string)$f); - - $div = $d->createElement('div'); - $div->appendChild($d->createTextNode('ook')); - $div->outerHTML = '
eek
'; - $this->assertSame('
ook
', (string)$div); - } - - - /** - * @covers \MensBeam\HTML\DOM\Element::__set_outerHTML - */ - public function testPropertySetOuterHTMLFailure(): void { - $this->expectException(DOMException::class); - $this->expectExceptionCode(DOMException::NO_MODIFICATION_ALLOWED); - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->outerHTML = 'FAIL'; - } - - - /** - * @covers \MensBeam\HTML\DOM\Element::getAttribute - */ - public function testGetAttribute(): void { - // Just need to test nonexistent attributes - $d = new Document(); - $d->appendChild($d->createElement('html')); - $this->assertNull($d->documentElement->getAttribute('ook')); - } - - - /** - * @covers \MensBeam\HTML\DOM\Element::getAttributeNodeNS - */ - public function testGetAttributeNodeNS(): void { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->setAttribute('ook', 'eek'); - // Empty string namespace - $ook = $d->documentElement->getAttributeNodeNS('', 'ook'); - $this->assertSame('eek', $ook->value); - // Bogus attribute - $this->assertNull($d->documentElement->getAttributeNodeNS(null, 'what')); - } - - - /** - * @covers \MensBeam\HTML\DOM\Element::getAttributeNS - */ - public function testGetAttributeNS(): void { - // Just need to test nonexistent attributes - $d = new Document(); - $d->appendChild($d->createElement('html')); - $this->assertNull($d->documentElement->getAttributeNS(Parser::HTML_NAMESPACE, 'ook')); - } - - - /** - * @covers \MensBeam\HTML\DOM\Element::hasAttributeNS - */ - public function testHasAttributeNS(): void { - // Just need to test empty string namespace - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->setAttribute('ook', 'eek'); - $this->assertTrue($d->documentElement->hasAttributeNS('', 'ook')); - } - - - /** - * @covers \MensBeam\HTML\DOM\Element::removeAttribute - */ - public function testRemoveAttribute(): void { - // Just need to test classList updates - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->classList->add('ook', 'eek'); - $d->documentElement->removeAttribute('class'); - $this->assertNull($d->documentElement->getAttribute('class')); - } - - - /** - * @covers \MensBeam\HTML\DOM\Element::removeAttributeNS - */ - public function testRemoveAttributeNS(): void { - // Just need to test classList updates - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->classList->add('ook', 'eek'); - $d->documentElement->removeAttributeNS(null, 'class'); - $this->assertNull($d->documentElement->getAttribute('class')); - - $d->documentElement->setAttributeNS(Parser::XMLNS_NAMESPACE, 'xmlns', Parser::HTML_NAMESPACE); - $d->documentElement->removeAttributeNS(Parser::XMLNS_NAMESPACE, 'xmlns'); - $this->assertNull($d->documentElement->getAttributeNS(Parser::XMLNS_NAMESPACE, 'xmlns')); - } - - - /** - * @covers \MensBeam\HTML\DOM\Element::setAttribute - * @covers \MensBeam\HTML\DOM\TokenList::add - */ - public function testSetAttribute(): void { - // Need to test classList updates - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->classList->add('ook', 'eek'); - $d->documentElement->setAttribute('class', 'ack'); - $this->assertSame('ack', $d->documentElement->classList[0]); - // Test setting class to empty string - $d->documentElement->setAttribute('class', ''); - $this->assertSame('', $d->documentElement->getAttribute('class')); - // Test setting id attribute - $d->documentElement->setAttribute('id', 'ook'); - $this->assertSame('ook', $d->documentElement->getAttribute('id')); - } - - - /** - * @covers \MensBeam\HTML\DOM\Element::setAttribute - * @covers \MensBeam\HTML\DOM\TokenList::add - */ - public function testSetAttributeFailure(): void { - $this->expectException(DOMException::class); - $this->expectExceptionCode(DOMException::INVALID_CHARACTER); - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->setAttribute('ook eek', 'fail'); - } - - - /** - * @covers \MensBeam\HTML\DOM\Element::setAttributeNS - * @covers \MensBeam\HTML\DOM\TokenList::add - */ - public function testSetAttributeNS(): void { - $d = new Document(); - // Don't append html element and set attribute - $de = $d->createElement('html'); - $de->setAttributeNS(null, 'id', 'ook'); - $this->assertSame('ook', $de->getAttribute('id')); - $de->setAttributeNS(null, 'class', 'ook'); - $this->assertSame('ook', $de->getAttribute('class')); - - $de->setAttributeNS(Parser::XMLNS_NAMESPACE, 'xmlns', Parser::HTML_NAMESPACE); - $this->assertSame(Parser::HTML_NAMESPACE, $de->getAttributeNS(Parser::XMLNS_NAMESPACE, 'xmlns')); - - $b = $d->createElement('body'); - $b->setAttributeNS(Parser::XMLNS_NAMESPACE, 'xmlns', Parser::HTML_NAMESPACE); - $this->assertSame(Parser::HTML_NAMESPACE, $b->getAttributeNS(Parser::XMLNS_NAMESPACE, 'xmlns')); - - $t = $d->createElement('template'); - $t->setAttributeNS(Parser::XMLNS_NAMESPACE, 'xmlns', Parser::HTML_NAMESPACE); - $this->assertSame(Parser::HTML_NAMESPACE, $t->getAttributeNS(Parser::XMLNS_NAMESPACE, 'xmlns')); - - // Test name coercion when namespace is null - $de->setAttributeNS(null, 'poop💩', 'ook'); - $this->assertSame('ook', $de->getAttribute('poop💩')); - } -} \ No newline at end of file diff --git a/tests/cases/TestElementMap.php b/tests/cases/TestElementMap.php deleted file mode 100644 index 3fb4143..0000000 --- a/tests/cases/TestElementMap.php +++ /dev/null @@ -1,38 +0,0 @@ -createElement('template'); - $this->assertTrue(ElementMap::add($t)); - $this->assertFalse(ElementMap::add($t)); - } - - - /** @covers \MensBeam\HTML\DOM\ElementMap::delete */ - public function testDelete(): void { - $d = new Document(); - $t = $d->createElement('template'); - $this->assertTrue(ElementMap::add($t)); - $this->assertTrue(ElementMap::delete($t)); - $this->assertFalse(ElementMap::delete($t)); - } -} \ No newline at end of file diff --git a/tests/cases/TestLeafNode.php b/tests/cases/TestLeafNode.php deleted file mode 100644 index bc0798b..0000000 --- a/tests/cases/TestLeafNode.php +++ /dev/null @@ -1,49 +0,0 @@ -appendChild($d->createElement('fail')); - } ], - [ function($d, $n) { - $n->insertBefore($d->createElement('fail')); - } ], - [ function($d, $n) { - $n->removeChild($d->createElement('fail')); - } ], - [ function($d, $n) { - $n->replaceChild($d->createElement('fail2'), $d->createElement('fail')); - } ], - ]; - } - - /** - * @dataProvider provideDisabledMethods - * @covers \MensBeam\HTML\DOM\LeafNode::appendChild - * @covers \MensBeam\HTML\DOM\LeafNode::insertBefore - * @covers \MensBeam\HTML\DOM\LeafNode::removeChild - * @covers \MensBeam\HTML\DOM\LeafNode::replaceChild - */ - public function testDisabledMethods(\Closure $closure): void { - $this->expectException(DOMException::class); - $this->expectExceptionCode(DOMException::HIERARCHY_REQUEST_ERROR); - $d = new Document(); - $closure($d, $d->createTextNode('ook')); - } -} \ No newline at end of file diff --git a/tests/cases/TestNode.php b/tests/cases/TestNode.php new file mode 100644 index 0000000..1dff3a5 --- /dev/null +++ b/tests/cases/TestNode.php @@ -0,0 +1,40 @@ +appendChild($d->createElement('html')); + $d->documentElement->appendChild($d->createElement('body')); + $b = $d->body; + $b->appendChild($d->createElement('span')); + $b->appendChild($d->createTextNode('ook')); + + // Node::childNodes on Element + $childNodes = $d->body->childNodes; + $this->assertEquals(2, $childNodes->length); + $this->assertSame('SPAN', $childNodes[0]->nodeName); + + // Node::childNodes on Text + $childNodes = $d->body->lastChild->childNodes; + $this->assertEquals(0, $childNodes->length); + // Try it again to test caching + $childNodes = $d->body->lastChild->childNodes; + } +} \ No newline at end of file diff --git a/tests/cases/TestNodeList.php b/tests/cases/TestNodeList.php deleted file mode 100644 index 5433c5d..0000000 --- a/tests/cases/TestNodeList.php +++ /dev/null @@ -1,103 +0,0 @@ -expectException(Exception::class); - $this->expectExceptionCode(Exception::ARGUMENT_TYPE_ERROR); - $closure(); - } - - - /** @covers \MensBeam\HTML\DOM\NodeList::count */ - public function testCount(): void { - $d = new Document(); - $list = new NodeList([ - $d->createElement('ook'), - $d->createTextNode('eek'), - $d->createComment('ack') - ]); - $this->assertEquals(3, count($list)); - } - - - /** @covers \MensBeam\HTML\DOM\NodeList::item */ - public function testItem(): void { - $d = new Document(); - $list = new NodeList([ - $d->createElement('ook'), - $d->createTextNode('eek'), - $d->createComment('ack') - ]); - $this->assertNull($list->item(42)); - } - - - /** - * @covers \MensBeam\HTML\DOM\NodeList::current - * @covers \MensBeam\HTML\DOM\NodeList::item - * @covers \MensBeam\HTML\DOM\NodeList::key - * @covers \MensBeam\HTML\DOM\NodeList::next - * @covers \MensBeam\HTML\DOM\NodeList::rewind - * @covers \MensBeam\HTML\DOM\NodeList::offsetExists - * @covers \MensBeam\HTML\DOM\NodeList::offsetGet - * @covers \MensBeam\HTML\DOM\NodeList::valid - */ - public function testIteration(): void { - $d = new Document(); - $list = new NodeList([ - $d->createElement('ook'), - $d->createTextNode('eek'), - $d->createComment('ack') - ]); - - foreach ($list as $key => $node) { - $this->assertSame($node, $list[$key]); - // test offsetExists - $this->assertTrue(isset($list[$key])); - } - } - - - /** @covers \MensBeam\HTML\DOM\NodeList::__get_length */ - public function testPropertyGetLength(): void { - $d = new Document(); - $list = new NodeList([ - $d->createElement('ook'), - $d->createTextNode('eek'), - $d->createComment('ack') - ]); - $this->assertEquals(3, $list->length); - } -} \ No newline at end of file diff --git a/tests/cases/TestNodeTrait.php b/tests/cases/TestNodeTrait.php deleted file mode 100644 index 8f19bc9..0000000 --- a/tests/cases/TestNodeTrait.php +++ /dev/null @@ -1,167 +0,0 @@ -appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $d->body->innerHTML = '

Ook

Eek

Ook eek, ook?

'; - $m = $d->getElementsByTagName('main')->item(0); - $f = $d->getElementsByTagName('footer')->item(0); - $e = $d->getElementById('eek'); - $h2Id = $e->getAttributeNode('id'); - $h2Class = $e->getAttributeNode('class'); - $aHref = $d->getElementsByTagName('a')->item(0)->getAttributeNode('href'); - - $compareMainToBody = $m->compareDocumentPosition($d->body); - $this->assertEquals(10, $compareMainToBody); - $compareBodyToMain = $d->body->compareDocumentPosition($m); - $this->assertEquals(20, $compareBodyToMain); - $compareFooterToMain = $f->compareDocumentPosition($m); - $this->assertEquals(2, $compareFooterToMain); - $compareMainToFooter = $m->compareDocumentPosition($f); - $this->assertEquals(4, $compareMainToFooter); - $compareH2IdToAHref = $h2Id->compareDocumentPosition($aHref); - $this->assertEquals(4, $compareH2IdToAHref); - $compareH2IdToH2Class = $h2Id->compareDocumentPosition($h2Class); - $this->assertEquals(36, $compareH2IdToH2Class); - $compareH2ClassToH2Id = $h2Class->compareDocumentPosition($h2Id); - $this->assertEquals(34, $compareH2ClassToH2Id); - $this->assertEquals(0, $m->compareDocumentPosition($m)); - - $this->assertGreaterThan(0, $compareMainToBody & Document::DOCUMENT_POSITION_CONTAINS); - $this->assertGreaterThan(0, $compareMainToBody & Document::DOCUMENT_POSITION_PRECEDING); - $this->assertEquals(0, $compareMainToBody & Document::DOCUMENT_POSITION_FOLLOWING); - - $this->assertGreaterThan(0, $compareBodyToMain & Document::DOCUMENT_POSITION_CONTAINED_BY); - $this->assertGreaterThan(0, $compareBodyToMain & Document::DOCUMENT_POSITION_FOLLOWING); - $this->assertEquals(0, $compareBodyToMain & Document::DOCUMENT_POSITION_PRECEDING); - - $this->assertGreaterThan(0, $compareFooterToMain & Document::DOCUMENT_POSITION_PRECEDING); - $this->assertGreaterThan(0, $compareMainToFooter & Document::DOCUMENT_POSITION_FOLLOWING); - - $this->assertGreaterThan(0, $compareH2IdToAHref & Document::DOCUMENT_POSITION_FOLLOWING); - $this->assertGreaterThan(0, $compareH2IdToH2Class & Document::DOCUMENT_POSITION_FOLLOWING); - $this->assertGreaterThan(0, $compareH2ClassToH2Id & Document::DOCUMENT_POSITION_PRECEDING); - - $m->parentNode->removeChild($m); - $compareDetachedMainToFooter = $m->compareDocumentPosition($f); - $this->assertEquals($compareDetachedMainToFooter, $m->compareDocumentPosition($f)); - $this->assertGreaterThanOrEqual(35, $compareDetachedMainToFooter); - $this->assertLessThanOrEqual(37, $compareDetachedMainToFooter); - $this->assertNotEquals(36, $compareDetachedMainToFooter); - } - - - /** @covers \MensBeam\HTML\DOM\NodeTrait::contains */ - public function testContains(): void { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $o = $d->body->appendChild($d->createTextNode('Ook!')); - $d2 = new Document(); - $d2->appendChild($d2->createElement('html')); - - $this->assertTrue($d->documentElement->contains($d->body)); - $this->assertTrue($d->contains($o)); - $this->assertFalse($o->contains($d)); - $this->assertFalse($d->contains($d2->documentElement)); - } - - - public function provideDisabledMethods(): iterable { - return [ - [ 'C14N' ], - [ 'C14NFile', 'ook' ], - [ 'getLineNo' ] - ]; - } - - /** - * @dataProvider provideDisabledMethods - * @covers \MensBeam\HTML\DOM\NodeTrait::C14N - * @covers \MensBeam\HTML\DOM\NodeTrait::C14NFile - * @covers \MensBeam\HTML\DOM\NodeTrait::getLineNo - */ - public function testDisabledMethods(string $methodName, ...$arguments): void { - $this->expectException(Exception::class); - $this->expectExceptionCode(Exception::DISABLED_METHOD); - $d = new Document(); - $d->$methodName(...$arguments); - } - - - /** @covers \MensBeam\HTML\DOM\NodeTrait::getRootNode */ - public function testGetRootNode(): void { - $d = new Document(); - $t = $d->createElement('template'); - $div = $t->content->appendChild($d->createElement('div')); - $this->assertTrue($t->content->isSameNode($div->getRootNode())); - } - - /** @covers \MensBeam\HTML\DOM\NodeTrait::isEqualNode */ - public function testIsEqualNode(): void { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $d->body->innerHTML = '

Ook

Eek

'; - - $d2 = new Document(); - $d2->appendChild($d2->createElement('html')); - $d2->documentElement->appendChild($d2->createElement('body')); - $d2->body->innerHTML = '

Ook

Eek

'; - - $this->assertTrue($d->isEqualNode($d2)); - - $d = new Document(); - $de = $d->createElement('html'); - $this->assertFalse($d->isEqualNode($de)); - - $d = new Document(); - $d->appendChild($d->implementation->createDocumentType('html', '', '')); - - $d2 = new Document(); - $d2->appendChild($d2->implementation->createDocumentType('ook', 'eek', 'ack')); - $this->assertFalse($d->isEqualNode($d2)); - - $d = new Document('Ook!

Eek

'); - $d2 = new Document('Eek!

Eek

'); - $this->assertFalse($d->isEqualNode($d2)); - - $d = new Document(); - $f = $d->createDocumentFragment(); - $f->appendChild($d->createElement('span')); - $f->appendChild($d->createTextNode('Ook')); - - $f2 = $d->createDocumentFragment(); - $f2->appendChild($d->createElement('span')); - $this->assertFalse($f->isEqualNode($f2)); - - $s = $d->createElement('span'); - $s->setAttribute('id', 'ook'); - $s2 = $d->createElement('span'); - $s2->setAttribute('class', 'ook'); - $this->assertFalse($s->isEqualNode($s2)); - - $s = $d->createElement('span'); - $br = $d->createElement('br'); - $this->assertFalse($s->isEqualNode($br)); - } -} \ No newline at end of file diff --git a/tests/cases/TestParentNode.php b/tests/cases/TestParentNode.php deleted file mode 100644 index 9ad50a5..0000000 --- a/tests/cases/TestParentNode.php +++ /dev/null @@ -1,289 +0,0 @@ -appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $div = $d->body->appendChild($d->createElement('div')); - $ook = $d->body->insertBefore($d->createTextNode('ook'), $div); - - $this->assertSame('ook
', (string)$d->body); - - $t = $d->body->insertBefore($d->createElement('template'), $ook); - - $this->assertSame('ook
', (string)$d->body); - $this->assertTrue(ElementMap::has($t)); - } - - - public function providePreInsertionValidationFailures(): iterable { - return [ - [ function() { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $b = $d->documentElement->appendChild($d->createElement('body')); - $b->appendChild($d->documentElement); - } ], - [ function() { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $b = $d->documentElement->appendChild($d->createElement('body')); - $t = $b->appendChild($d->createElement('template')); - $t->content->appendChild($b); - } ], - [ function() { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $b = $d->documentElement->appendChild($d->createElement('body')); - $d->insertBefore($d->createElement('fail'), $b); - }, DOMException::NOT_FOUND ], - [ function() { - $d = new Document(); - $df = $d->createDocumentFragment(); - $df->appendChild($d->createElement('html')); - $df->appendChild($d->createTextNode(' ')); - $d->appendChild($df); - } ], - [ function() { - $d = new Document(); - $d->appendChild($d); - } ], - [ function() { - $d = new Document(); - $d->appendChild($d->implementation->createDocumentType('html')); - $d->appendChild($d->implementation->createDocumentType('html')); - } ], - [ function() { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->appendChild($d->implementation->createDocumentType('html')); - } ], - [ function() { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $c = $d->appendChild($d->createComment('ook')); - $d->insertBefore($d->implementation->createDocumentType('html'), $c); - } ], - [ function() { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->insertBefore($d->implementation->createDocumentType('html')); - } ], - [ function() { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->insertBefore($d->implementation->createDocumentType('html')); - } ], - [ function() { - $d = new Document(); - $dt = $d->appendChild($d->implementation->createDocumentType('html')); - $df = $d->createDocumentFragment(); - $df->appendChild($d->createElement('html')); - $d->insertBefore($df, $dt); - } ], - [ function() { - $d = new Document(); - $c = $d->appendChild($d->createComment('OOK')); - $d->appendChild($d->implementation->createDocumentType('html')); - $df = $d->createDocumentFragment(); - $df->appendChild($d->createElement('html')); - $d->insertBefore($df, $c); - } ], - [ function() { - $d = new Document(); - $dt = $d->appendChild($d->implementation->createDocumentType('html')); - $d->insertBefore($d->createElement('html'), $dt); - } ], - [ function() { - $d = new Document(); - $c = $d->appendChild($d->createComment('OOK')); - $d->appendChild($d->implementation->createDocumentType('html')); - $d->insertBefore($d->createElement('html'), $c); - } ], - [ function() { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->appendChild($d->createElement('body')); - } ] - ]; - } - - /** - * @dataProvider providePreInsertionValidationFailures - * @covers \MensBeam\HTML\DOM\DOMException::__construct - * @covers \MensBeam\HTML\DOM\NodeTrait::getRootNode - * @covers \MensBeam\HTML\DOM\ParentNode::preInsertionValidity - */ - public function testPreInsertionValidationFailures(\Closure $closure, int $errorCode = DOMException::HIERARCHY_REQUEST_ERROR): void { - $this->expectException(DOMException::class); - $this->expectExceptionCode($errorCode); - $closure(); - } - - - /** @covers \MensBeam\HTML\DOM\ParentNode::__get_children */ - public function testPropertyGetChildren(): void { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - - $this->assertSame(1, $d->documentElement->children->length); - } - - - /** - * @covers \MensBeam\HTML\DOM\ParentNode::querySelector - * @covers \MensBeam\HTML\DOM\ParentNode::querySelectorAll - * @covers \MensBeam\HTML\DOM\ParentNode::scopeMatchSelector - * @covers \MensBeam\HTML\DOM\NodeList::__construct - * @covers \MensBeam\HTML\DOM\NodeList::item - */ - public function testQuerySelector(): void { - $d = new Document('
ook
eek
'); - $div = $d->body->querySelector('div'); - $this->assertSame('div', $div->nodeName); - $this->assertNull($d->querySelector('body::before')); - - $divs = $d->body->querySelectorAll('div'); - $this->assertEquals(2, $divs->length); - $this->assertSame('eek', $divs->item(1)->getAttribute('id')); - $this->assertNull($d->querySelector('.ook')); - $this->assertEquals(0, $d->querySelectorAll('body::before')->length); - } - - - /** - * @covers \MensBeam\HTML\DOM\ParentNode::querySelector - * @covers \MensBeam\HTML\DOM\ParentNode::querySelectorAll - * @covers \MensBeam\HTML\DOM\ParentNode::scopeMatchSelector - */ - public function testQuerySelectorFailure(): void { - $this->expectException(DOMException::class); - $this->expectExceptionCode(DOMException::SYNTAX_ERROR); - $d = new Document(); - $d->querySelector('ook?'); - } - - - /** @covers \MensBeam\HTML\DOM\ParentNode::replaceChild */ - public function testReplaceChild(): void { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $div = $d->body->appendChild($d->createElement('div')); - $ook = $d->body->replaceChild($d->createTextNode('ook'), $div); - - $this->assertSame('ook', (string)$d->body); - - $t = $d->body->replaceChild($d->createElement('template'), $ook); - - $this->assertSame('', (string)$d->body); - $this->assertTrue(ElementMap::has($t)); - - $d->body->replaceChild($d->createElement('br'), $t); - - $this->assertSame('
', (string)$d->body); - $this->assertFalse(ElementMap::has($t)); - } - - - /** @covers \MensBeam\HTML\DOM\ParentNode::replaceChildren */ - public function testReplaceChildren(): void { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $div = $d->body->appendChild($d->createElement('div')); - $ook = $d->body->appendChild($d->createTextNode('ook'), $d->body->appendChild($d->createElement('div'))); - - $d->body->replaceChildren($d->createElement('br'), 'ook', $d->createElement('span'), 'eek'); - $this->assertSame('
ookeek', (string)$d->body); - - $d->body->replaceChildren('ook'); - $this->assertSame('ook', (string)$d->body); - } - - - /** @covers \MensBeam\HTML\DOM\ParentNode::walk */ - public function testWalk(): void { - // Test removal of elements when walking - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $d->body->innerHTML = 'OOOK'; - $spans = $d->body->walk(function($n) { - return ($n instanceof Element && $n->nodeName === 'span'); - }); - - foreach ($spans as $s) { - if ($s->getAttribute('class') === 'three') { - $s->parentNode->removeChild($s); - } - } - - $this->assertSame('OOK', (string)$d->body); - - // Test walking through templates' content - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $t = $d->body->appendChild($d->createElement('template')); - $t->content->appendChild($d->createElement('ook')); - - $this->assertSame('ook', $d->body->walk(function($n) { - return ($n instanceof Element && $n->nodeName === 'ook'); - })->current()->nodeName); - } - - - public function provideWalkFailures(): iterable { - $d = new Document(); - $d->appendChild($d->createElement('html')); - $d->documentElement->appendChild($d->createElement('body')); - $d->body->innerHTML = '

Ook

Eek

Ook eek, ook?

'; - - return [ - [ function() use ($d) { - $d->body->walk(function($n) { - return 'ook'; - })->current(); - } ], - [ function() use ($d) { - $d->body->walk(function($n) { - return new \DateTime(); - })->current(); - } ] - ]; - } - - /** - * @dataProvider provideWalkFailures - * @covers \MensBeam\HTML\DOM\DOMException::__construct - * @covers \MensBeam\HTML\DOM\ParentNode::walk - */ - public function testWalkFailures(\Closure $closure): void { - $this->expectException(Exception::class); - $this->expectExceptionCode(Exception::CLOSURE_RETURN_TYPE_ERROR); - $closure(); - } -} \ No newline at end of file diff --git a/tests/cases/TestTokenList.php b/tests/cases/TestTokenList.php deleted file mode 100644 index 58b4247..0000000 --- a/tests/cases/TestTokenList.php +++ /dev/null @@ -1,230 +0,0 @@ -createElement('html'); - $e->classList->add(''); - }, DOMException::SYNTAX_ERROR ], - [ function() { - $d = new Document(); - $e = $d->createElement('html'); - $e->classList->remove(''); - }, DOMException::SYNTAX_ERROR ], - [ function() { - $d = new Document(); - $e = $d->createElement('html'); - $e->classList->replace('ack', ''); - }, DOMException::SYNTAX_ERROR ], - [ function() { - $d = new Document(); - $e = $d->createElement('html'); - $e->classList->toggle(''); - }, DOMException::SYNTAX_ERROR ], - [ function() { - $d = new Document(); - $e = $d->createElement('html'); - $e->classList->add('fail fail'); - }, DOMException::INVALID_CHARACTER ], - [ function() { - $d = new Document(); - $e = $d->createElement('html'); - $e->classList->remove('fail fail'); - }, DOMException::INVALID_CHARACTER ], - [ function() { - $d = new Document(); - $e = $d->createElement('html'); - $e->classList->replace('ack', 'fail fail'); - }, DOMException::INVALID_CHARACTER ], - [ function() { - $d = new Document(); - $e = $d->createElement('html'); - $e->classList->toggle('fail fail'); - }, DOMException::INVALID_CHARACTER ] - ]; - } - - /** - * @dataProvider provideAddRemoveReplaceToggleFailures - * @covers \MensBeam\HTML\DOM\TokenList::add - * @covers \MensBeam\HTML\DOM\TokenList::remove - * @covers \MensBeam\HTML\DOM\TokenList::replace - * @covers \MensBeam\HTML\DOM\TokenList::toggle - */ - public function testAddRemoveReplaceFailures(\Closure $closure, int $errorCode): void { - $this->expectException(DOMException::class); - $this->expectExceptionCode($errorCode); - $closure(); - } - - - /** @covers \MensBeam\HTML\DOM\TokenList::contains */ - public function testContains(): void { - $d = new Document(); - $e = $d->appendChild($d->createElement('html')); - $e->classList->add('ook', 'eek', 'ack', 'ookeek'); - $this->assertTrue($e->classList->contains('ack')); - $this->assertFalse($e->classList->contains('fail')); - } - - - /** @covers \MensBeam\HTML\DOM\TokenList::count */ - public function testCount(): void { - $d = new Document(); - $e = $d->appendChild($d->createElement('html')); - $e->classList->add('ook', 'eek', 'ack', 'ookeek'); - $this->assertSame(4, count($e->classList)); - } - - - /** @covers \MensBeam\HTML\DOM\TokenList::item */ - public function testItem(): void { - $d = new Document(); - $e = $d->appendChild($d->createElement('html')); - $e->classList->add('ook', 'eek', 'ack', 'ookeek'); - $this->assertNull($e->classList->item(42)); - } - - - /** - * @covers \MensBeam\HTML\DOM\TokenList::current - * @covers \MensBeam\HTML\DOM\TokenList::item - * @covers \MensBeam\HTML\DOM\TokenList::key - * @covers \MensBeam\HTML\DOM\TokenList::next - * @covers \MensBeam\HTML\DOM\TokenList::rewind - * @covers \MensBeam\HTML\DOM\TokenList::offsetExists - * @covers \MensBeam\HTML\DOM\TokenList::offsetGet - * @covers \MensBeam\HTML\DOM\TokenList::valid - */ - public function testIteration(): void { - $d = new Document(); - $e = $d->appendChild($d->createElement('html')); - $e->classList->add('ook', 'eek', 'ack', 'ookeek'); - - foreach ($e->classList as $key => $className) { - $this->assertSame($className, $e->classList[$key]); - // test offsetExists - $this->assertTrue(isset($e->classList[$key])); - } - } - - /** @covers \MensBeam\HTML\DOM\TokenList::__get_length */ - public function testPropertyGetLength(): void { - // Test it with and without an attached document element - $d = new Document(); - $e = $d->appendChild($d->createElement('html')); - $e->classList->add('ook', 'eek', 'ack', 'ookeek'); - $this->assertSame(4, $e->classList->length); - - $d = new Document(); - $e = $d->createElement('html'); - $e->classList->add('ook', 'eek', 'ack', 'ookeek'); - $this->assertSame(4, $e->classList->length); - } - - - /** - * @covers \MensBeam\HTML\DOM\TokenList::__get_value - * @covers \MensBeam\HTML\DOM\TokenList::__set_value - */ - public function testPropertyGetSetValue(): void { - // Test it with and without an attached document element - $d = new Document(); - $e = $d->createElement('html'); - $e->classList->add('ook', 'eek', 'ack', 'ookeek'); - $this->assertSame('ook eek ack ookeek', $e->classList->value); - $this->assertSame('ook eek ack ookeek', $e->getAttribute('class')); - $e->classList->value = 'omg wtf bbq lol zor bor xxx'; - $this->assertSame('lol', $e->classList[3]); - $this->assertSame('omg wtf bbq lol zor bor xxx', $e->classList->value); - $this->assertSame('omg wtf bbq lol zor bor xxx', $e->getAttribute('class')); - $e->classList->value = ''; - $this->assertSame('', $e->classList->value); - $this->assertSame('', $e->getAttribute('class')); - - $d = new Document(); - $e = $d->appendChild($d->createElement('html')); - $e->classList->add('ook', 'eek', 'ack', 'ookeek'); - $this->assertSame('ook eek ack ookeek', $e->classList->value); - $this->assertSame('ook eek ack ookeek', $e->getAttribute('class')); - $e->classList->value = 'omg wtf bbq lol zor bor xxx'; - $this->assertSame('lol', $e->classList[3]); - $this->assertSame('omg wtf bbq lol zor bor xxx', $e->classList->value); - $this->assertSame('omg wtf bbq lol zor bor xxx', $e->getAttribute('class')); - $e->classList->value = ''; - $this->assertSame('', $e->classList->value); - $this->assertSame('', $e->getAttribute('class')); - } - - - /** @covers \MensBeam\HTML\DOM\TokenList::replace */ - public function testReplace(): void { - $d = new Document(); - $e = $d->appendChild($d->createElement('html')); - $e->setAttribute('class', 'ook eek ack ookeek'); - $this->assertTrue($e->classList->replace('ack', 'what')); - $this->assertSame('ook eek what ookeek', $e->classList->value); - $this->assertSame('ook eek what ookeek', $e->getAttribute('class')); - $this->assertFalse($e->classList->replace('fail', 'eekook')); - } - - - /** @covers \MensBeam\HTML\DOM\TokenList::remove */ - public function testRemove(): void { - $d = new Document(); - $e = $d->appendChild($d->createElement('html')); - $e->setAttribute('class', 'ook eek ack ookeek'); - $e->classList->remove('ack'); - $this->assertSame('ook eek ookeek', $e->classList->value); - $this->assertSame('ook eek ookeek', $e->getAttribute('class')); - } - - - /** @covers \MensBeam\HTML\DOM\TokenList::supports */ - public function testSupports(): void { - $d = new Document(); - $e = $d->appendChild($d->createElement('html')); - $e->classList->add('ook', 'eek', 'ack', 'ookeek'); - $this->assertTrue($e->classList->supports('ack')); - } - - - /** @covers \MensBeam\HTML\DOM\TokenList::toggle */ - public function testToggle(): void { - $d = new Document(); - $e = $d->appendChild($d->createElement('html')); - $e->setAttribute('class', 'ook eek ack ookeek'); - $this->assertFalse($e->classList->toggle('ack')); - $this->assertSame('ook eek ookeek', $e->classList->value); - $this->assertSame('ook eek ookeek', $e->getAttribute('class')); - $this->assertTrue($e->classList->toggle('ack')); - $this->assertSame('ook eek ookeek ack', $e->classList->value); - $this->assertSame('ook eek ookeek ack', $e->getAttribute('class')); - $this->assertTrue($e->classList->toggle('ack', true)); - $this->assertSame('ook eek ookeek ack', $e->classList->value); - $this->assertSame('ook eek ookeek ack', $e->getAttribute('class')); - $this->assertFalse($e->classList->toggle('eekook', false)); - $this->assertSame('ook eek ookeek ack', $e->classList->value); - $this->assertSame('ook eek ookeek ack', $e->getAttribute('class')); - $this->assertTrue($e->classList->toggle('eekook', true)); - $this->assertSame('ook eek ookeek ack eekook', $e->classList->value); - $this->assertSame('ook eek ookeek ack eekook', $e->getAttribute('class')); - } -} \ No newline at end of file diff --git a/tests/cases/serializer/TestSerializer.php b/tests/cases/serializer/TestSerializer.php deleted file mode 100644 index 974b1cc..0000000 --- a/tests/cases/serializer/TestSerializer.php +++ /dev/null @@ -1,279 +0,0 @@ -append(new \GlobIterator(\MensBeam\HTML\DOM\BASE."tests/cases/Serializer/standard/*.dat", \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME)); - foreach ($files as $file) { - if (!in_array(basename($file), $blacklist)) { - yield from $this->parseTreeTestFile($file); - } - } - } - - /** - * @dataProvider provideStandardTreeTests - * @covers \MensBeam\HTML\DOM\Document::saveHTML - * @covers \MensBeam\HTML\DOM\Document::blockElementFilterFactory - * @covers \MensBeam\HTML\DOM\Document::serializeNode - * @covers \MensBeam\HTML\DOM\Document::__toString - * @covers \MensBeam\HTML\DOM\ToString::__toString - */ - public function testStandardTreeTests(array $data, bool $fragment, string $exp): void { - $node = $this->buildTree($data, $fragment); - $this->assertSame($exp, (string)$node); - } - - - public function provideFormattedTreeTests(): iterable { - $files = new \AppendIterator(); - $files->append(new \GlobIterator(\MensBeam\HTML\DOM\BASE."tests/cases/Serializer/formatted/*.dat", \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME)); - foreach ($files as $file) { - yield from $this->parseTreeTestFile($file); - } - } - - /** - * @dataProvider provideFormattedTreeTests - * @covers \MensBeam\HTML\DOM\Document::saveHTML - * @covers \MensBeam\HTML\DOM\Document::blockElementFilterFactory - * @covers \MensBeam\HTML\DOM\Document::serializeNode - * @covers \MensBeam\HTML\DOM\Document::__toString - * @covers \MensBeam\HTML\DOM\ToString::__toString - */ - public function testFormattedTreeTests(array $data, bool $fragment, string $exp): void { - $node = $this->buildTree($data, $fragment, true); - $this->assertSame($exp, (string)$node); - } - - - /** - * @covers \MensBeam\HTML\DOM\Document::saveHTML - * @covers \MensBeam\HTML\DOM\Document::serializeNode - */ - public function testSerializingDocumentType(): void { - $d = new Document(); - $dt = $d->implementation->createDocumentType('ook', 'eek', 'ack'); - $d->appendChild($dt); - $this->assertSame('', $d->saveHTML($dt)); - } - - /** - * @covers \MensBeam\HTML\DOM\Document::saveHTML - * @covers \MensBeam\HTML\DOM\Document::serializeNode - */ - public function testSerializingDocumentFragments(): void { - $d = new Document(); - $d->formatOutput = true; - $df = $d->createDocumentFragment(); - $df->appendChild($d->createTextNode('ook')); - $this->assertSame('ook', (string)$df); - $this->assertSame('ook', $d->saveHTML($df)); - } - - /** - * @covers \MensBeam\HTML\DOM\Document::saveHTML - * @covers \MensBeam\HTML\DOM\Document::serializeNode - * @covers \MensBeam\HTML\DOM\ToString::__toString - */ - public function testSerializingElements(): void { - $d = new Document(); - $i = $d->createElement('input'); - $i->appendChild($d->createTextNode('You should not see this text')); - $this->assertSame('', (string)$i); - $this->assertSame('', $d->saveHTML($i)); - - $t = $d->createElement('template'); - $t->setAttribute('ook', 'eek'); - $t->content->appendChild($d->createTextNode('Ook!')); - $this->assertSame('', (string)$t); - $this->assertSame('Ook!', $d->saveHTML($t)); - } - - /** - * @covers \MensBeam\HTML\DOM\Document::saveHTML - * @covers \MensBeam\HTML\DOM\Document::serializeNode - * @covers \MensBeam\HTML\DOM\ToString::__toString - */ - public function testSerializingTextNodes(): void { - $d = new Document(); - $d->formatOutput = true; - $i = $d->createTextNode('test'); - $this->assertSame('test', (string)$i); - $this->assertSame('test', $d->saveHTML($i)); - } - - - public function provideSerializerFailures(): iterable { - return [ - [ function() { - $d = new Document(); - $h = $d->createElement('html'); - $d2 = new Document(); - $d2->saveHTML($h); - }, DOMException::class, DOMException::WRONG_DOCUMENT ], - [ function() { - $d = new Document(); - $d->saveHTML($d->createAttribute('fail')); - }, Exception::class, Exception::ARGUMENT_TYPE_ERROR ], - [ function() { - $d = new Document(); - $d->saveHTML(new \DOMDocument()); - }, Exception::class, Exception::ARGUMENT_TYPE_ERROR ], - [ function() { - $d = new Document(); - $d2 = new \DOMDocument(); - $d->saveHTML($d2->createComment('fail')); - }, Exception::class, Exception::ARGUMENT_TYPE_ERROR ] - ]; - } - - /** - * @dataProvider provideSerializerFailures - * @covers \MensBeam\HTML\DOM\Document::saveHTML - */ - public function testSerializerFailures(\Closure $closure, string $exceptionClassName, int $errorCode): void { - $this->expectException($exceptionClassName); - $this->expectExceptionCode($errorCode); - $closure(); - } - - - - - protected function buildTree(array $data, bool $fragment, bool $formatOutput = false): \DOMNode { - $document = new Document; - $document->formatOutput = $formatOutput; - if ($fragment) { - $document->appendChild($document->createElement("html")); - $out = $document->createDocumentFragment(); - } else { - $out = $document; - } - $cur = $out; - $pad = 2; - // process each line in turn - for ($l = 0; $l < sizeof($data); $l++) { - preg_match('/^(\|\s+)(.+)/', $data[$l], $m); - // pop any parents as long as the padding of the line is less than the expected padding - $p = strlen((string) $m[1]); - assert($p >= 2 && $p <= $pad && !($p % 2), new \Exception("Input data is invalid on line ".($l + 1))); - while ($p < $pad) { - $pad -= 2; - $cur = $cur->parentNode; - } - // act based upon what the rest of the line looks like - $d = $m[2]; - if (preg_match('/^$/', $d, $m)) { - // comment - $cur->appendChild($document->createComment($m[1])); - } elseif (preg_match('/^]*)(?: "([^"]*)" "([^"]*)")?)?>$/', $d, $m)) { - // doctype - $name = strlen((string) ($m[1] ?? "")) ? $m[1] : " "; - $public = strlen((string) ($m[2] ?? "")) ? $m[2] : ""; - $system = strlen((string) ($m[3] ?? "")) ? $m[3] : ""; - $cur->appendChild($document->implementation->createDocumentType($name, $public, $system)); - } elseif (preg_match('/^<\?([^ ]+) ([^>]*)>$/', $d, $m)) { - $cur->appendChild($document->createProcessingInstruction($m[1], $m[2])); - } elseif (preg_match('/^<(?:([^ ]+) )?([^>]+)>$/', $d, $m)) { - // element - $ns = strlen((string) $m[1]) ? (array_flip(Parser::NAMESPACE_MAP)[$m[1]] ?? $m[1]) : null; - $cur = $cur->appendChild($document->createElementNS($ns, $m[2])); - $pad += 2; - } elseif (preg_match('/^(?:([^" ]+) )?([^"=]+)="((?:[^"]|"(?!$))*)"$/', $d, $m)) { - // attribute - $ns = strlen((string) $m[1]) ? (array_flip(Parser::NAMESPACE_MAP)[$m[1]] ?? $m[1]) : ""; - - if ($ns === '') { - $cur->setAttribute($m[2], $m[3]); - } else { - $cur->setAttributeNS($ns, $m[2], $m[3]); - } - } elseif (preg_match('/^"((?:[^"]|"(?!$))*)("?)$/', $d, $m)) { - // text - $t = $m[1]; - while (!strlen((string) $m[2])) { - preg_match('/^((?:[^"]|"(?!$))*)("?)$/', $data[++$l], $m); - $t .= "\n".$m[1]; - } - $cur->appendChild($document->createTextNode($t)); - } else { - throw new \Exception("Input data is invalid on line ".($l + 1)); - } - } - return $out; - } - - protected function parseTreeTestFile(string $file): \Generator { - $index = 0; - $l = 0; - $lines = array_map(function($v) { - return rtrim($v, "\n"); - }, file($file)); - while ($l < sizeof($lines)) { - $pos = $l + 1; - assert(in_array($lines[$l], ["#document", "#fragment"]), new \Exception("Test $file #$index does not start with #document or #fragment tag at line ".($l + 1))); - $fragment = $lines[$l] === "#fragment"; - // collect the test input - $data = []; - for (++$l; $l < sizeof($lines); $l++) { - if (preg_match('/^#(script-(on|off)|output)$/', $lines[$l])) { - break; - } - $data[] = $lines[$l]; - } - // set the script mode, if present - assert(preg_match('/^#(script-(on|off)|output)$/', $lines[$l]) === 1, new \Exception("Test $file #$index follows data with something other than script flag or output at line ".($l + 1))); - $script = null; - if ($lines[$l] === "#script-off") { - $script = false; - $l++; - } elseif ($lines[$l] === "#script-on") { - $script = true; - $l++; - } - // collect the output string - $exp = []; - assert($lines[$l] === "#output", new \Exception("Test $file #$index follows input with something other than output at line ".($l + 1))); - for (++$l; $l < sizeof($lines); $l++) { - if ($lines[$l] === "" && in_array(($lines[$l + 1] ?? ""), ["#document", "#fragment"])) { - break; - } - assert(preg_match('/^([^#]|$)/', $lines[$l]) === 1, new \Exception("Test $file #$index contains unrecognized data after output at line ".($l + 1))); - $exp[] = $lines[$l]; - } - $exp = implode("\n", $exp); - if (!$script) { - yield basename($file)." #$index (line $pos)" => [$data, $fragment, $exp]; - } - $l++; - $index++; - } - } -} diff --git a/tests/cases/serializer/formatted/mensbeam01.dat b/tests/cases/serializer/formatted/mensbeam01.dat deleted file mode 100644 index 6751566..0000000 --- a/tests/cases/serializer/formatted/mensbeam01.dat +++ /dev/null @@ -1,153 +0,0 @@ -#fragment -| -#output - - -#document -| -| -| -#output - - - - -#document -| -| -| -| -#output - - - - - - - -#document -| -| -|
-|       
-#output
-
- 
-  
- - - -#document -| -| -| -#output - - - - -#fragment -| -|
-| -#output - -
- - - - -#fragment -| -|
-| -| -#output - -
- - - - - - -#document -| -| -| " -" -| -| -| "ook -" -#output - - - - - ook - - - -#fragment -|
-|
-| " -" -#output -
-
-
- -#fragment -|
-| -|
-| -#output - - -#fragment -|
-| -|
-#output -
- - -
-
- -#fragment -|
-|
-| -|
-#output -
-
- - - -
-
- -#fragment -|
-|
-| -|
-#output -
-
- - - -
-
\ No newline at end of file diff --git a/tests/cases/serializer/standard/mensbeam01.dat b/tests/cases/serializer/standard/mensbeam01.dat deleted file mode 100644 index c317644..0000000 --- a/tests/cases/serializer/standard/mensbeam01.dat +++ /dev/null @@ -1,33 +0,0 @@ -#fragment -| -#output - - -#fragment -| -| test💩test="test" -#output - - -#fragment -| -| "You should not see this text." -#output - - -#fragment -| -| class="test" -#output - - -#fragment -| -#output - - -#fragment -| -| poop💩="soccer" -#output - diff --git a/tests/cases/serializer/standard/mensbeam02.dat b/tests/cases/serializer/standard/mensbeam02.dat deleted file mode 100644 index 7760020..0000000 --- a/tests/cases/serializer/standard/mensbeam02.dat +++ /dev/null @@ -1,34 +0,0 @@ -#document -| -#output - - -#document -| -| -#output - - -#document -| -| -#output - - -#document -| -| -#output - - -#document -| -| -#output - - -#document -| -| -#output - diff --git a/tests/cases/serializer/standard/wpt01.dat b/tests/cases/serializer/standard/wpt01.dat deleted file mode 100644 index 0074d36..0000000 --- a/tests/cases/serializer/standard/wpt01.dat +++ /dev/null @@ -1,913 +0,0 @@ -#fragment -| -#output - - -#fragment -| -| -#output - - -#fragment -| -| -| b="c" -#output - - -#fragment -| -| -| b="&" -#output - - -#fragment -| -| -| b=" " -#output - - -#fragment -| -| -| b=""" -#output - - -#fragment -| -| -| b="<" -#output - - -#fragment -| -| -| b=">" -#output - - -#fragment -| -| -| href="javascript:"<>"" -#output - - -#fragment -| -| -| xlink xlink:href="a" -#output - - -#fragment -| -| -| xmlns xmlns:svg="test" -#output - - -#fragment -| -| "a" -#output -a - -#fragment -| -| "&" -#output -& - -#fragment -| -| " " -#output -  - -#fragment -| -| "<" -#output -< - -#fragment -| -| ">" -#output -> - -#fragment -| -| """ -#output -" - -#fragment -| -| - -#fragment -| -| - -#fragment -| - -#fragment -| -| -| "<&>" -#output -<span><xmp><&> - -#fragment -| -| - -#fragment -| -| -| "<&>" -#output -<span><noembed><&> - -#fragment -| -| -| "<&>" -#output -<span><noframes><&> - -#fragment -| -|