Browse Source

Serializer 100% covered -- FFS

master
Dustin Wilson 2 years ago
parent
commit
5bfd8c82ec
  1. 4
      composer.lock
  2. 36
      lib/Serializer.php
  3. 116
      tests/cases/TestSerializer.php
  4. 4
      tests/phpunit.dist.xml

4
composer.lock

@ -63,7 +63,7 @@
"source": {
"type": "git",
"url": "mensbeam-gitea:MensBeam/HTML-Parser.git",
"reference": "a8435f7c358faaf39f9a0daa7f7a331a3e7cffca"
"reference": "b90806860312fad8f6da763656784a8a6cf880b0"
},
"require": {
"ext-dom": "*",
@ -130,7 +130,7 @@
"parsing",
"whatwg"
],
"time": "2021-12-05T15:00:39+00:00"
"time": "2021-12-16T15:29:51+00:00"
},
{
"name": "mensbeam/intl",

36
lib/Serializer.php

@ -16,6 +16,10 @@ use MensBeam\HTML\Parser\{
class Serializer extends ParserSerializer {
protected static function fragmentHasHost(\DOMDocumentFragment $fragment): bool {
return (Reflection::getProtectedProperty($fragment->ownerDocument->getWrapperNode($fragment), 'host') !== null);
}
protected static function getTemplateContent(\DOMElement $node): \DOMNode {
return Reflection::getProtectedProperty($node->ownerDocument->getWrapperNode($node)->content, 'innerNode');
}
@ -39,12 +43,16 @@ class Serializer extends ParserSerializer {
}
protected static function treatAsBlockWithTemplates(\DOMNode $node): bool {
$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);
$parent = $node->parentNode ?? $node;
$document = $node->ownerDocument;
$xpath = $document->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"])]', $parent);
foreach ($templates as $t) {
$content = Reflection::getProtectedProperty($t->ownerDocument->getWrapperNode($t)->content, 'innerNode');
if ($xpath->evaluate(self::BLOCK_QUERY, $content) > 0) {
$content = static::getTemplateContent($t);
$result = ($xpath->evaluate(self::BLOCK_QUERY, $content) > 0);
if ($result || static::treatAsBlockWithTemplates($content)) {
return true;
}
}
@ -56,24 +64,14 @@ class Serializer extends ParserSerializer {
// NOTE: This method is used only when pretty printing. Implementors of userland
// PHP DOM solutions with template contents will need to extend this method to
// be able to moonwalk through document fragment hosts.
$n = $node;
do {
if ($n instanceof \DOMDocumentFragment) {
$host = Reflection::getProtectedProperty($node->ownerDocument->getWrapperNode($n), 'host');
if ($host !== null) {
$n = Reflection::getProtectedProperty($host->get(), 'innerNode');
} else {
return true;
}
} else {
if ($n->parentNode !== null && ($n->parentNode->namespaceURI ?? Parser::HTML_NAMESPACE) !== Parser::HTML_NAMESPACE) {
continue;
}
if ($n->parentNode !== null && ($n->parentNode->namespaceURI ?? Parser::HTML_NAMESPACE) !== Parser::HTML_NAMESPACE) {
continue;
}
if (self::treatAsBlock($n->parentNode)) {
return true;
}
if (self::treatAsBlock($n->parentNode)) {
return true;
}
break;

116
tests/cases/TestSerializer.php

@ -18,4 +18,120 @@ use MensBeam\HTML\DOM\{
/** @covers \MensBeam\HTML\DOM\Serializer */
class TestSerializer extends \PHPUnit\Framework\TestCase {
public function testMethod_isPreformattedContent(): void {
$d = new Document('<pre><code></code></pre>');
$this->assertSame(<<<HTML
<html>
<head></head>
<body>
<pre><code></code></pre>
</body>
</html>
HTML, $d->serialize(null, [ 'reformatWhitespace' => true ]));
$frag = $d->createDocumentFragment();
$p = $frag->appendChild($d->createElement('pre'));
$t = $p->appendChild($d->createElement('template'));
$t->content->appendChild($d->createTextNode('ook'));
$t->content->appendChild($d->createElement('br'));
$this->assertSame('<pre><template>ook<br></template></pre>', $d->serialize($frag, [ 'reformatWhitespace' => true ]));
$div = $t->content->appendChild($d->createElement('div'));
$div->appendChild($d->createTextNode('ook'));
$this->assertSame('<pre><template>ook<br><div>ook</div></template></pre>', $d->serialize($frag, [ 'reformatWhitespace' => true ]));
}
public function testMethod_treatAsBlockWithTemplates(): void {
$d = new Document('<span>ook</span><template><div>ook</div></template>');
$this->assertSame(<<<HTML
<body>
<span>ook</span>
<template>
<div>ook</div>
</template>
</body>
HTML, $d->serialize($d->body, [ 'reformatWhitespace' => true ]));
}
public function provideMethod_treatForeignRootAsBlock(): iterable {
return [
[
function() {
$d = new Document(<<<HTML
<!DOCTYPE html>
<html>
<body>
<span><template><svg><g><rect id="eek--a" width="5" height="5"/></g></svg><div>ook</div></template></span>
</body>
</html>
HTML, 'UTF-8');
return $d->serialize($d->getElementsByTagName('template')[0]->content->firstChild->firstChild, [ 'reformatWhitespace' => true ]);
},
<<<HTML
<g>
<rect id="eek--a" width="5" height="5"></rect>
</g>
HTML
],
[
function() {
$d = new Document(<<<HTML
<!DOCTYPE html>
<html>
<body>
<svg role="img" viewBox="0 0 26 26"><title>Ook</title>
<rect id="eek--a" width="5" height="5"/></svg>
</body>
</html>
HTML, 'UTF-8');
return $d->serialize($d->body, [ 'reformatWhitespace' => true ]);
},
<<<HTML
<body><svg role="img" viewBox="0 0 26 26"><title>Ook</title> <rect id="eek--a" width="5" height="5"></rect></svg></body>
HTML
],
[
function() {
$d = new Document(<<<HTML
<!DOCTYPE html>
<html>
<body>
<svg><g><g><rect id="eek--a" width="5" height="5"/></g></g></svg>
<div></div>
</body>
</html>
HTML, 'UTF-8');
$svg = $d->getElementsByTagNameNS(Node::SVG_NAMESPACE, 'svg')[0];
$g = $svg->firstChild->firstChild;
return $d->serialize($g, [ 'reformatWhitespace' => true ]);
},
<<<HTML
<g>
<rect id="eek--a" width="5" height="5"></rect>
</g>
HTML
],
];
}
/** @dataProvider provideMethod_treatForeignRootAsBlock */
public function testMethod_treatForeignRootAsBlock(\Closure $closure, string $expected): void {
$this->assertSame($expected, $closure());
}
}

4
tests/phpunit.dist.xml

@ -33,8 +33,8 @@
<file>cases/TestParentNode.php</file>
<file>cases/TestProcessingInstruction.php</file>
</testsuite>
<!--<testsuite name="Serializer">
<testsuite name="Serializer">
<file>cases/TestSerializer.php</file>
</testsuite>-->
</testsuite>
</testsuites>
</phpunit>

Loading…
Cancel
Save