Browse Source

More tests, starting to fill in Document

wrapper-classes
Dustin Wilson 2 years ago
parent
commit
c94c0d9f8f
  1. 61
      lib/DOMImplementation.php
  2. 15
      lib/DOMTokenList.php
  3. 125
      lib/Document.php
  4. 14
      lib/DocumentOrElement.php
  5. 2
      lib/Node.php
  6. 2
      lib/Serializer.php
  7. 165
      tests/cases/TestDOMImplementation.php
  8. 28
      tests/cases/TestDocument.php
  9. 1
      tests/phpunit.dist.xml

61
lib/DOMImplementation.php

@ -15,10 +15,23 @@ use MensBeam\HTML\DOM\Inner\{
class DOMImplementation {
protected \WeakReference $document;
protected static array $documentsCache = [];
public function __construct(?Document $document = null) {
$this->document = \WeakReference::create($document ?? new Document());
if ($document !== null) {
$this->document = \WeakReference::create($document);
} else {
// Under most uses of this class the document is a weak reference to a stored
// Document object. When creating an implementation without an existing Document
// this class will create a weak reference to a newly-instantiated Document
// class. This presents a problem because that new Document is immediately
// garbage collected because there's no longer a reference pointing to it,
// making the weak reference return null instead of the Document it should be
// returning. To circumvent this we're going to have a static documents cache
// that's used to keep a reference around.
$this->document = \WeakReference::create(self::$documentsCache[] = new Document());
}
}
@ -54,12 +67,12 @@ class DOMImplementation {
# 7. document’s content type is determined by namespace:
switch ($namespace) {
# ↪ HTML namespace
case self::HTML_NAMESPACE:
case Node::HTML_NAMESPACE:
# application/xhtml+xml
$contentType = 'application/xhtml+xml';
break;
# ↪ SVG namespace
case self::SVG_NAMESPACE:
case Node::SVG_NAMESPACE:
# image/svg+xml
$contentType = 'image/svg+xml';
break;
@ -92,16 +105,50 @@ class DOMImplementation {
// PHP's DOM won't accept an empty string as the qualifiedName, so use a space
// instead which won't be encountered elsewhere because it violates the QName
// production; this will be worked around in DocumentType.
return $innerDocument->getWrapperNode($innerDocument->implementation->createDocumentType($qualifiedName, $publicId, $systemId));
return $innerDocument->getWrapperNode($innerDocument->implementation->createDocumentType(($qualifiedName !== '') ? $qualifiedName : ' ', $publicId, $systemId));
}
public function createHTMLDocument(string $title = ''): Document {
$document = Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\Document');
# The createHTMLDocument(title) method steps are:
#
# 1. Let doc be a new document that is an HTML document.
$doc = Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\Document');
# 2. Set doc’s content type to "text/html".
// Already done because of how this library is to be used.
# 3. Append a new doctype, with "html" as its name and with its node document
# set to doc, to doc.
$doc->appendChild($doc->implementation->createDocumentType('html', '', ''));
# 4. Append the result of creating an element given doc, html, and the HTML
# namespace, to doc.
$documentElement = $doc->appendChild($doc->createElement('html'));
# 5. Append the result of creating an element given doc, head, and the HTML
# namespace, to the html element created earlier.
$head = $documentElement->appendChild($doc->createElement('head'));
# 6. If title is given:
if ($title !== '') {
$document->title = $title;
# 1. Append the result of creating an element given doc, title, and the HTML
# namespace, to the head element created earlier.
$t = $head->appendChild($doc->createElement('title'));
# 2. Append a new Text node, with its data set to title (which could be the empty
# string) and its node document set to doc, to the title element created earlier.
$t->appendChild($doc->createTextNode($title));
}
return $document;
# 7. Append the result of creating an element given doc, body, and the HTML
# namespace, to the html element created earlier.
$documentElement->appendChild($doc->createElement('body'));
# 8. doc’s origin is this’s associated document’s origin.
// Not necessary. No scripting in this implementation.
# 9. Return doc.
return $doc;
}
public function hasFeature(): bool {

15
lib/DOMTokenList.php

@ -7,7 +7,8 @@
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
use MensBeam\Framework\MagicProperties,
use MensBeam\HTML\Parser\Data,
MensBeam\Framework\MagicProperties,
MensBeam\HTML\DOM\Inner\Reflection;
@ -24,8 +25,6 @@ class DOMTokenList implements \ArrayAccess, \Countable, \Iterator {
# empty.
protected array $tokenSet = [];
private const ASCII_WHITESPACE_REGEX = '/[\t\n\x0c\r ]+/';
protected function __get_length(): int {
return $this->_length;
@ -103,7 +102,7 @@ class DOMTokenList implements \ArrayAccess, \Countable, \Iterator {
# 2. If token contains any ASCII whitespace, then throw an
# "InvalidCharacterError" DOMException.
if (preg_match(self::ASCII_WHITESPACE_REGEX, $token)) {
if (preg_match(Data::WHITESPACE_REGEX, $token)) {
throw new DOMException(DOMException::INVALID_CHARACTER);
}
}
@ -184,7 +183,7 @@ class DOMTokenList implements \ArrayAccess, \Countable, \Iterator {
# 2. If token contains any ASCII whitespace, then throw an
# "InvalidCharacterError" DOMException.
if (preg_match(self::ASCII_WHITESPACE_REGEX, $token)) {
if (preg_match(Data::WHITESPACE_REGEX, $token)) {
throw new DOMException(DOMException::INVALID_CHARACTER);
}
}
@ -218,7 +217,7 @@ class DOMTokenList implements \ArrayAccess, \Countable, \Iterator {
# 2. If either token or newToken contains any ASCII whitespace, then throw an
# "InvalidCharacterError" DOMException.
if (preg_match(self::ASCII_WHITESPACE_REGEX, $token) || preg_match(self::ASCII_WHITESPACE_REGEX, $newToken)) {
if (preg_match(Data::WHITESPACE_REGEX, $token) || preg_match(Data::WHITESPACE_REGEX, $newToken)) {
throw new DOMException(DOMException::INVALID_CHARACTER);
}
@ -276,7 +275,7 @@ class DOMTokenList implements \ArrayAccess, \Countable, \Iterator {
# 2. If token contains any ASCII whitespace, then throw an
# "InvalidCharacterError" DOMException.
if (preg_match(self::ASCII_WHITESPACE_REGEX, $token)) {
if (preg_match(Data::WHITESPACE_REGEX, $token)) {
throw new DOMException(DOMException::INVALID_CHARACTER);
}
@ -317,7 +316,7 @@ class DOMTokenList implements \ArrayAccess, \Countable, \Iterator {
#
# 1. Let inputTokens be the result of splitting input on ASCII whitespace.
// There isn't a Set object in php, so make sure all the tokens are unique.
$inputTokens = array_unique(preg_split(self::ASCII_WHITESPACE_REGEX, $input));
$inputTokens = array_unique(preg_split(Data::WHITESPACE_REGEX, $input));
# 2. Let tokens be a new ordered set.
# 3. For each token in inputTokens, append token to tokens.

125
lib/Document.php

@ -89,6 +89,129 @@ class Document extends Node {
return $this->_characterSet;
}
protected function __get_title(): string {
# The title attribute must, on getting, run the following algorithm:
# 1. If the document element is an SVG svg element, then let value be the child text
# content of the first SVG title element that is a child of the document element.
$value = '';
$documentElement = $this->innerNode->documentElement;
if ($documentElement === null) {
return '';
}
if ($documentElement->namespaceURI === Node::SVG_NAMESPACE && $documentElement->tagName === 'svg') {
$children = $documentElement->childNodes;
foreach ($children as $child) {
if ($child instanceof \DOMElement && $child->namespaceURI === Node::SVG_NAMESPACE && $child->tagName === 'title') {
$value = $child->textContent ?? '';
break;
}
}
}
# 2. Otherwise, let value be the child text content of the title element, or the
# empty string if the title element is null.
else {
# The title element of a document is the first title element in the document (in
# tree order), if there is one, or null otherwise.
$title = $this->innerNode->getElementsByTagName('title');
if ($title->length > 0) {
$value = $title->item(0)->textContent ?? '';
}
}
# 3. Strip and collapse ASCII whitespace in value.
# 4. Return value.
return ($value !== '') ? trim(preg_replace(Data::WHITESPACE_REGEX, ' ', $value), Data::WHITESPACE) : '';
}
protected function __set_title(string $value): void {
# On setting, the steps corresponding to the first matching condition in the following list must be run:
#
# If the document element is an SVG svg element
$documentElement = $this->innerNode->documentElement;
if ($documentElement === null) {
return;
}
# If the document element is an SVG svg element
if ($documentElement->namespaceURI === Node::SVG_NAMESPACE && $documentElement->tagName === 'svg') {
# 1. If there is an SVG title element that is a child of the document element,
# let element be the first such element.
$element = null;
$children = $documentElement->childNodes;
foreach ($children as $child) {
if ($child instanceof \DOMElement && $child->namespaceURI === Node::SVG_NAMESPACE && $child->tagName === 'title') {
$element = $child;
break;
}
}
# 2. Otherwise:
if ($element === null) {
# 1. Let element be the result of creating an element given the document element's
# node document, title, and the SVG namespace.
$element = $this->innerNode->createElementNS(Node::SVG_NAMESPACE, 'title');
# 2. Insert element as the first child of the document element.
$this->innerNode->documentElement->appendChild($element);
}
# 3. String replace all with the given value within element.
// This is basically what textContent will do for us...
$element->textContent = $value;
}
# If the document element is in the HTML namespace
elseif ($this->documentElement->namespaceURI === Node::HTML_NAMESPACE) {
# 1. If the title element is null and the head element is null, then return.
# The title element of a document is the first title element in the document (in
# tree order), if there is one, or null otherwise.
$title = null;
$element = null;
$titles = $this->innerNode->getElementsByTagName('title');
if ($titles->length > 0) {
$title = $titles->item(0);
}
# The head element of a document is the first head element that is a child of
# the html element, if there is one, or null otherwise.
$head = null;
$children = $documentElement->childNodes;
foreach ($children as $child) {
if ($child instanceof \DOMElement && $child->namespaceURI === null && $child->tagName === 'head') {
$head = $child;
break;
}
}
if ($title === null && $head === null) {
return;
}
# 2. If the title element is non-null, let element be the title element.
if ($title !== null) {
$element = $title;
}
# 3. Otherwise:
else {
# 1. Let element be the result of creating an element given the document
# element's node document, title, and the HTML namespace.
$element = $this->innerNode->createElementNS(Node::SVG_NAMESPACE, 'title');
# 2. Append element to the head element.
$head->appendChild($element);
}
# 4. String replace all with the given value within element.
// This is basically what textContent will do for us...
if ($element !== null) {
$element->textContent = $value;
}
}
# Otherwise
# Do nothing.
}
protected function __get_URL(): string {
return $this->_URL;
}
@ -301,7 +424,7 @@ class Document extends Node {
return $this->innerNode->getWrapperNode($element);
}
public function createElementNS(string $namespace, string $qualifiedName): Element {
public function createElementNS(?string $namespace, string $qualifiedName): Element {
# The internal createElementNS steps, given document, namespace, qualifiedName,
# and options, are as follows:
// DEVIATION: The options parameter is at present only used for custom elements.

14
lib/DocumentOrElement.php

@ -68,7 +68,7 @@ trait DocumentOrElement {
}
$query = substr($query, 0, -4) . ']';
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\HTMLCollection', $doc, (new \DOMXPath($doc))->query($query, $innerNode));
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\HTMLCollection', $doc, $doc->xpath->query($query, $innerNode));
}
public function getElementsByTagName(string $qualifiedName): HTMLCollection {
@ -81,7 +81,7 @@ trait DocumentOrElement {
# 1. If qualifiedName is U+002A (*), then return a HTMLCollection rooted at
# root, whose filter matches only descendant elements.
if ($qualifiedName === '*') {
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\HTMLCollection', $doc, (new \DOMXPath($doc))->query('.//*', $this->innerNode));
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\HTMLCollection', $doc, $doc->xpath->query('.//*', $this->innerNode));
}
# 2. Otherwise, if root’s node document is an HTML document, return a
@ -94,7 +94,7 @@ trait DocumentOrElement {
if (!$wrapperDoc instanceof XMLDocument) {
// Because of a PHP DOM bug all HTML namespaced elements use null internally as
// their namespace.
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\HTMLCollection', $doc, (new \DOMXPath($doc))->query('.//' . strtolower($qualifiedName) . "[namespace-uri()=''] | .//{$qualifiedName}[not(namespace-uri()='')]", $this->innerNode));
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\HTMLCollection', $doc, $doc->xpath->query('.//' . strtolower($qualifiedName) . "[namespace-uri()=''] | .//{$qualifiedName}[not(namespace-uri()='')]", $this->innerNode));
}
# 3. Otherwise, return a HTMLCollection rooted at root, whose filter matches
@ -128,19 +128,19 @@ trait DocumentOrElement {
# 2. If both namespace and localName are U+002A (*), then return a HTMLCollection
# rooted at root, whose filter matches descendant elements.
if ($namespace === '*' && $localName === '*') {
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\HTMLCollection', $doc, (new \DOMXPath($doc))->query('.//*', $this->innerNode));
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\HTMLCollection', $doc, $doc->xpath->query('.//*', $this->innerNode));
}
# 3. If namespace is U+002A (*), then return a HTMLCollection rooted at root, whose
# filter matches descendant elements whose local name is localName.
if ($namespace === '*') {
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\HTMLCollection', $doc, (new \DOMXPath($doc))->query(".//*[local-name()='$localName']", $this->innerNode));
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\HTMLCollection', $doc, $doc->xpath->query(".//*[local-name()='$localName']", $this->innerNode));
}
# 4. If localName is U+002A (*), then return a HTMLCollection rooted at root, whose
# filter matches descendant elements whose namespace is namespace.
if ($localName === '*') {
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\HTMLCollection', $doc, (new \DOMXPath($doc))->query(".//*[namespace-uri()='$namespace']", $this->innerNode));
return Reflection::createFromProtectedConstructor(__NAMESPACE__ . '\\HTMLCollection', $doc, $doc->xpath->query(".//*[namespace-uri()='$namespace']", $this->innerNode));
}
# 5. Return a HTMLCollection rooted at root, whose filter matches descendant
@ -196,7 +196,7 @@ trait DocumentOrElement {
# 10. Return namespace, prefix, and localName.
return [
// Internally HTML namespaced elements in HTML documents use null because of a PHP DOM bug.
'namespace' => (!$this->getInnerDocument() instanceof XMLDocument && $namespace === self::HTML_NAMESPACE) ? null : $namespace,
'namespace' => (!$this instanceof XMLDocument && $namespace === self::HTML_NAMESPACE) ? null : $namespace,
'prefix' => $prefix,
'localName' => $localName
];

2
lib/Node.php

@ -1214,7 +1214,7 @@ abstract class Node {
// so their contents may be stored in the HTMLTemplateElement's content document
// fragment.
$doc = $this->getInnerDocument();
$templates = (new \DOMXPath($doc))->query('//template[not(ancestor::template)]', $contextNode);
$templates = $doc->xpath->query('//template[not(ancestor::template)]', $contextNode);
// Iterate in reverse to prevent the live nodelist from doing anything screwy
for ($templatesCount = count($templates), $i = $templatesCount - 1; $i >= 0; $i--) {
$t = $templates->item($i);

2
lib/Serializer.php

@ -39,7 +39,7 @@ class Serializer extends ParserSerializer {
}
protected static function treatAsBlockWithTemplates(\DOMNode $node): bool {
$xpath = new \DOMXPath($node->ownerDocument);
$xpath = $node->ownerDocument->xpath;
$templates = $xpath->query('.//template[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"][not(ancestor::iframe[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::listing[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::noembed[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::noframes[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::noscript[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::plaintext[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::pre[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::style[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::script[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::textarea[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::title[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"] or ancestor::xmp[namespace-uri()="" or namespace-uri()="http://www.w3.org/1999/xhtml"])]', $node);
foreach ($templates as $t) {

165
tests/cases/TestDOMImplementation.php

@ -0,0 +1,165 @@
<?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,
DOMImplementation,
Node
};
/** @covers \MensBeam\HTML\DOM\DOMImplementation */
class TestDOMImplementation extends \PHPUnit\Framework\TestCase {
/**
* @covers \MensBeam\HTML\DOM\DOMImplementation::createDocument
*
* @covers \MensBeam\HTML\DOM\Document::__construct
* @covers \MensBeam\HTML\DOM\Document::__get_contentType
* @covers \MensBeam\HTML\DOM\Document::__get_doctype
* @covers \MensBeam\HTML\DOM\Document::__get_documentElement
* @covers \MensBeam\HTML\DOM\Document::createElementNS
* @covers \MensBeam\HTML\DOM\DocumentOrElement::validateAndExtract
* @covers \MensBeam\HTML\DOM\DocumentType::__construct
* @covers \MensBeam\HTML\DOM\DocumentType::__get_name
* @covers \MensBeam\HTML\DOM\DOMImplementation::__construct
* @covers \MensBeam\HTML\DOM\DOMImplementation::createDocumentType
* @covers \MensBeam\HTML\DOM\Element::__construct
* @covers \MensBeam\HTML\DOM\Element::__get_localName
* @covers \MensBeam\HTML\DOM\Element::__get_namespaceURI
* @covers \MensBeam\HTML\DOM\Element::__get_prefix
* @covers \MensBeam\HTML\DOM\Element::__get_tagName
* @covers \MensBeam\HTML\DOM\Node::__construct
* @covers \MensBeam\HTML\DOM\Node::__get_nodeName
* @covers \MensBeam\HTML\DOM\Node::__get_ownerDocument
* @covers \MensBeam\HTML\DOM\Node::appendChild
* @covers \MensBeam\HTML\DOM\Node::getInnerDocument
* @covers \MensBeam\HTML\DOM\Node::getInnerNode
* @covers \MensBeam\HTML\DOM\Node::getRootNode
* @covers \MensBeam\HTML\DOM\Node::postInsertionBugFixes
* @covers \MensBeam\HTML\DOM\Node::preInsertionBugFixes
* @covers \MensBeam\HTML\DOM\Node::preInsertionValidity
* @covers \MensBeam\HTML\DOM\Inner\Document::__construct
* @covers \MensBeam\HTML\DOM\Inner\Document::__get_wrapperNode
* @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
* @covers \MensBeam\HTML\DOM\Inner\Reflection::getProtectedProperty
* @covers \MensBeam\HTML\DOM\Inner\Reflection::setProtectedProperties
*/
public function testMethod_createDocument(): void {
$di = new DOMImplementation();
$d = $di->createDocument(null, 'ook', $di->createDocumentType('ook', 'eek', 'ack'));
$this->assertNull($d->documentElement->namespaceURI);
$this->assertSame('ook', $d->documentElement->tagName);
$this->assertSame('ook', $d->doctype->nodeName);
$this->assertSame('application/xml', $d->contentType);
$d = $di->createDocument(Node::HTML_NAMESPACE, 'html', $di->createDocumentType('html', '', ''));
$this->assertSame(Node::HTML_NAMESPACE, $d->documentElement->namespaceURI);
$this->assertSame('html', $d->documentElement->tagName);
$this->assertSame('html', $d->doctype->nodeName);
$this->assertSame('application/xhtml+xml', $d->contentType);
$d = $di->createDocument(Node::SVG_NAMESPACE, 'svg', null);
$this->assertSame(Node::SVG_NAMESPACE, $d->documentElement->namespaceURI);
$this->assertSame('svg', $d->documentElement->tagName);
$this->assertNull($d->doctype);
$this->assertSame('image/svg+xml', $d->contentType);
}
/**
* @covers \MensBeam\HTML\DOM\DOMImplementation::createDocumentType
*
* @covers \MensBeam\HTML\DOM\Document::__construct
* @covers \MensBeam\HTML\DOM\DOMException::__construct
* @covers \MensBeam\HTML\DOM\DOMImplementation::__construct
* @covers \MensBeam\HTML\DOM\Node::__construct
* @covers \MensBeam\HTML\DOM\Inner\Document::__construct
*/
public function testMethod_createDocumentType__errors(): void {
$this->expectException(DOMException::class);
$this->expectExceptionCode(DOMException::INVALID_CHARACTER);
$di = new DOMImplementation();
$di->createDocumentType('fail fail', 'fail', 'fail');
}
/**
* @covers \MensBeam\HTML\DOM\DOMImplementation::createHTMLDocument
*
* @covers \MensBeam\HTML\DOM\Document::__construct
* @covers \MensBeam\HTML\DOM\Document::__get_contentType
* @covers \MensBeam\HTML\DOM\Document::__get_doctype
* @covers \MensBeam\HTML\DOM\Document::__get_documentElement
* @covers \MensBeam\HTML\DOM\Document::__get_implementation
* @covers \MensBeam\HTML\DOM\Document::__get_title
* @covers \MensBeam\HTML\DOM\Document::createElement
* @covers \MensBeam\HTML\DOM\Document::createTextNode
* @covers \MensBeam\HTML\DOM\DocumentType::__construct
* @covers \MensBeam\HTML\DOM\DocumentType::__get_name
* @covers \MensBeam\HTML\DOM\DOMImplementation::__construct
* @covers \MensBeam\HTML\DOM\DOMImplementation::createDocumentType
* @covers \MensBeam\HTML\DOM\Element::__construct
* @covers \MensBeam\HTML\DOM\Element::__get_localName
* @covers \MensBeam\HTML\DOM\Element::__get_namespaceURI
* @covers \MensBeam\HTML\DOM\Element::__get_prefix
* @covers \MensBeam\HTML\DOM\Element::__get_tagName
* @covers \MensBeam\HTML\DOM\Node::__construct
* @covers \MensBeam\HTML\DOM\Node::__get_nodeName
* @covers \MensBeam\HTML\DOM\Node::__get_ownerDocument
* @covers \MensBeam\HTML\DOM\Node::appendChild
* @covers \MensBeam\HTML\DOM\Node::getInnerDocument
* @covers \MensBeam\HTML\DOM\Node::getInnerNode
* @covers \MensBeam\HTML\DOM\Node::getRootNode
* @covers \MensBeam\HTML\DOM\Node::postInsertionBugFixes
* @covers \MensBeam\HTML\DOM\Node::preInsertionBugFixes
* @covers \MensBeam\HTML\DOM\Node::preInsertionValidity
* @covers \MensBeam\HTML\DOM\Text::__construct
* @covers \MensBeam\HTML\DOM\Inner\Document::__construct
* @covers \MensBeam\HTML\DOM\Inner\Document::__get_wrapperNode
* @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
* @covers \MensBeam\HTML\DOM\Inner\Reflection::getProtectedProperty
* @covers \MensBeam\HTML\DOM\Inner\Reflection::setProtectedProperties
*/
public function testMethod_createHTMLDocument(): void {
$di = new DOMImplementation();
$d = $di->createHTMLDocument('ook');
$this->assertSame(Node::HTML_NAMESPACE, $d->documentElement->namespaceURI);
$this->assertSame('html', $d->documentElement->tagName);
$this->assertSame('html', $d->doctype->nodeName);
$this->assertSame('text/html', $d->contentType);
$this->assertSame('ook', $d->title);
}
/**
* @covers \MensBeam\HTML\DOM\DOMImplementation::hasFeature
*
* @covers \MensBeam\HTML\DOM\Document::__construct
* @covers \MensBeam\HTML\DOM\DOMImplementation::__construct
* @covers \MensBeam\HTML\DOM\Node::__construct
* @covers \MensBeam\HTML\DOM\Inner\Document::__construct
*/
public function testMethod_hasFeature(): void {
$di = new DOMImplementation();
$this->assertTrue($di->hasFeature());
}
}

28
tests/cases/TestDocument.php

@ -11,6 +11,7 @@ namespace MensBeam\HTML\DOM\TestCase;
use MensBeam\HTML\DOM\{
Document,
DOMException,
DOMImplementation,
Element,
Node,
Text,
@ -484,4 +485,31 @@ class TestDocument extends \PHPUnit\Framework\TestCase {
$d->loadFile('https://google.com');
$this->assertSame('https://google.com', $d->documentURI);
}
public function testProperty_title() {
$d = new Document();
$this->assertSame('', $d->title);
$d->title = 'fail';
$this->assertSame('', $d->title);
$d = (new DOMImplementation)->createDocument(Node::SVG_NAMESPACE, 'svg');
$this->assertSame('', $d->title);
$d->title = 'Ook';
$this->assertSame('Ook', $d->title);
$d->title = ' Ee k ';
$this->assertSame('Ee k', $d->title);
$d = new Document();
$de = $d->appendChild($d->createElement('html'));
$d->title = 'Ook';
$this->assertSame('', $d->title);
$de->appendChild($d->createElement('head'));
$d->title = 'Ook';
$this->assertSame('Ook', $d->title);
$d->title = 'Eek';
$this->assertSame('Eek', $d->title);
}
}

1
tests/phpunit.dist.xml

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

Loading…
Cancel
Save