PHP Classes

File: specs/FluidXml.php

Recommend this page to a friend!
  Classes of Daniele Orlando   FluidXML   specs/FluidXml.php   Download  
File: specs/FluidXml.php
Role: Unit test script
Content type: text/plain
Description: Unit test script
Class: FluidXML
Manipulate XML documents using a fluent interface
Author: By
Last change: Merge pull request #24 from alepeino/any-ns-css-selector

FluidXML supports CSS 'any namespace' selector.

Thanks to @alepeino.
CSS selector for namespace wildcard
Refactor CssTranslator tests
Date: 7 years ago
Size: 132,439 bytes
 

Contents

Class file image Download
<?php require_once __DIR__ . DIRECTORY_SEPARATOR . '.common.php'; use \FluidXml\FluidXml; use \FluidXml\FluidNamespace; use \FluidXml\FluidHelper; use \FluidXml\FluidContext; use \FluidXml\FluidDocument; use \FluidXml\FluidInsertionHandler; use \FluidXml\FluidRepeater; use const \FluidXml\FLUIDXML_VERSION; use function \FluidXml\fluidxml; use function \FluidXml\fluidns; use function \FluidXml\fluidify; describe('FLUIDXML_VERSION', function () { it('should be defined', function () { // Require for the codecoverage analysis. require \dirname(__DIR__).'/source/FluidXml.php'; $actual = \defined('FLUIDXML_VERSION'); $expected = true; \assert($actual === $expected, __($actual, $expected)); }); }); describe('fluidxml()', function () { it('should behave like FluidXml::__construct()', function () { $xml = new FluidXml(); $alias = fluidxml(); $actual = $alias->xml(); $expected = $xml->xml(); \assert($actual === $expected, __($actual, $expected)); $options = [ 'root' => 'root', 'version' => '1.2', 'encoding' => 'UTF-16', 'stylesheet' => 'stylesheet.xsl' ]; $xml = new FluidXml(null, $options); $alias = fluidxml(null, $options); $actual = $alias->xml(); $expected = $xml->xml(); \assert($actual === $expected, __($actual, $expected)); }); }); describe('fluidify()', function () { it('should behave like FluidXml::load()', function () { $ds = \DIRECTORY_SEPARATOR; $file = __DIR__ . "{$ds}..{$ds}sandbox{$ds}.test_fluidify.xml"; $doc = "<root>\n" . " <parent>content</parent>\n" . "</root>"; \file_put_contents($file, $doc); $xml = FluidXml::load($file); $alias = fluidify($file); \unlink($file); $actual = $alias->xml(); $expected = $xml->xml(); \assert($actual === $expected, __($actual, $expected)); }); }); describe('fluidns()', function () { it('should behave like FluidNamespace::__construct()', function () { $ns = new FluidNamespace('x', 'x.com'); $alias = fluidns('x', 'x.com'); $actual = $ns->id(); $expected = $alias->id(); \assert($actual === $expected, __($actual, $expected)); $actual = $ns->uri(); $expected = $alias->uri(); \assert($actual === $expected, __($actual, $expected)); $actual = $ns->mode(); $expected = $alias->mode(); \assert($actual === $expected, __($actual, $expected)); $ns = new FluidNamespace('x', 'x.com', FluidNamespace::MODE_IMPLICIT); $alias = fluidns('x', 'x.com', FluidNamespace::MODE_IMPLICIT); $actual = $ns->id(); $expected = $alias->id(); \assert($actual === $expected, __($actual, $expected)); $actual = $ns->uri(); $expected = $alias->uri(); \assert($actual === $expected, __($actual, $expected)); $actual = $ns->mode(); $expected = $alias->mode(); \assert($actual === $expected, __($actual, $expected)); }); }); describe('FluidXml', function () { $ds = \DIRECTORY_SEPARATOR; $this->out_dir = __DIR__ . "{$ds}..{$ds}sandbox{$ds}"; it('should throw invoking not existing staic method', function () { try { FluidXml::lload(); } catch (\Exception $e) { $actual = $e; } assert_is_a($actual, \Exception::class); }); describe(':load()', function () { it('should import an XML file', function () { $file = "{$this->out_dir}.test_load.xml"; $doc = "<root>\n" . " <parent>content</parent>\n" . "</root>"; \file_put_contents($file, $doc); $xml = FluidXml::load($file); \unlink($file); $expected = $doc; assert_equal_xml($xml, $expected); }); it('should throw for not existing file', function () { $err_handler = \set_error_handler(function () {}); try { $xml = FluidXml::load('.impossible.xml'); } catch (\Exception $e) { $actual = $e; } \set_error_handler($err_handler); assert_is_a($actual, \Exception::class); }); }); if (\version_compare(\phpversion(), '7', '>=')) { describe(':new()', function () { it('should behave like FluidXml::__construct()', function () { $xml = new FluidXml(); eval('$alias = \FluidXml\FluidXml::new();'); $actual = $alias->xml(); $expected = $xml->xml(); \assert($actual === $expected, __($actual, $expected)); $options = [ 'root' => 'root', 'version' => '1.2', 'encoding' => 'UTF-16', 'stylesheet' => 'stylesheet.xsl' ]; $xml = new FluidXml($options); eval('$alias = \FluidXml\FluidXml::new($options);'); $actual = $alias->xml(); $expected = $xml->xml(); \assert($actual === $expected, __($actual, $expected)); }); }); } describe('.__construct()', function () { $doc = "<root>\n" . " <parent>content</parent>\n" . "</root>"; $dom = new \DOMDocument(); $dom->loadXML($doc); $stylesheet = "<?xml-stylesheet type=\"text/xsl\" " . "encoding=\"UTF-8\" " . "indent=\"yes\" " . "href=\"http://servo-php.org/fluidxml\"?>"; it('should create an UTF-8 XML-1.0 document with one default root element', function () { $xml = new FluidXml(); $expected = "<doc/>"; assert_equal_xml($xml, $expected); }); it('should create an UTF-8 XML-1.0 document with one custom root element as first or second argument', function () { $xml = new FluidXml('document'); $expected = "<document/>"; assert_equal_xml($xml, $expected); $xml = new FluidXml(null, ['root' => 'document']); assert_equal_xml($xml, $expected); }); it('should create an UTF-8 XML-1.0 document with no root element as first or second argument', function () { $xml = new FluidXml(null); $expected = ""; assert_equal_xml($xml, $expected); $xml = new FluidXml(null, ['root' => null]); assert_equal_xml($xml, $expected); $xml = new FluidXml('doc', ['root' => null]); assert_equal_xml($xml, $expected); }); it('should create an UTF-8 XML-1.0 document with a stylesheet and a root element', function () use ($stylesheet) { $xml = new FluidXml('doc', ['stylesheet' => 'http://servo-php.org/fluidxml']); $expected = $stylesheet . "\n" . "<doc/>"; assert_equal_xml($xml, $expected); }); it('should create an UTF-8 XML-1.0 document with a stylesheet and no root element', function () use ($stylesheet) { $xml = new FluidXml(null, ['stylesheet' => 'http://servo-php.org/fluidxml']); $expected = $stylesheet; assert_equal_xml($xml, $expected); }); it('should import an XML string', function () use ($doc, $dom) { $exp = $dom->saveXML(); // This $exp has the XML header. // The first empty line is used to test the trim of the string. $xml = new FluidXml("\n " . $exp); $expected = $doc; assert_equal_xml($xml, $expected); // This $exp is deprived of the XML header. $xml = new FluidXml("\n " . \substr($exp, \strpos($exp, "\n") + 1)); $expected = $doc; assert_equal_xml($xml, $expected); }); it('should import an array of elements with the @ syntax', function () { $xml = new FluidXml(['root' => [ 'child1' => [ '@id' => 1 ], 'child2' => 'Text 2' ] ]); $expected = "<root>\n" . " <child1 id=\"1\"/>\n" . " <child2>Text 2</child2>\n" . "</root>"; assert_equal_xml($xml, $expected); }); it('should import a DOMDocument', function () use ($doc, $dom) { $xml = new FluidXml($dom); $expected = $doc; assert_equal_xml($xml, $expected); }); it('should import a DOMNode', function () use ($dom) { $domxp = new \DOMXPath($dom); $nodes = $domxp->query('/root/parent'); $xml = new FluidXml($nodes[0]); $expected = "<parent>content</parent>"; assert_equal_xml($xml, $expected); }); it('should import a DOMNodeList', function () use ($dom) { $domxp = new \DOMXPath($dom); $nodes = $domxp->query('/root/parent'); $xml = new FluidXml($nodes); $expected = "<parent>content</parent>"; assert_equal_xml($xml, $expected); }); it('should import a SimpleXMLElement', function () use ($doc, $dom) { $xml = new FluidXml(\simplexml_import_dom($dom)); $expected = $doc; assert_equal_xml($xml, $expected); }); it('should import a FluidXml', function () use ($doc) { $xml = new FluidXml(new FluidXml($doc)); $expected = $doc; assert_equal_xml($xml, $expected); }); it('should import a FluidContext', function () use ($doc) { $cx = (new FluidXml($doc))->query('/root'); $xml = new FluidXml($cx); $expected = $doc; assert_equal_xml($xml, $expected); }); it('should throw for not supported documents', function () { try { $xml = new FluidXml(1); } catch (\Exception $e) { $actual = $e; } assert_is_a($actual, \Exception::class); }); it('should throw invoking not existing method', function () { $xml = new FluidXml(); try { $xml->qquery(); } catch (\Exception $e) { $actual = $e; } assert_is_a($actual, \Exception::class); }); }); describe('.namespace()', function () { it('should be fluid', function () { assert_is_fluid('namespace', 'a', 'b'); }); it('should accept a namespace', function () { $xml = new FluidXml(); $x_ns = new FluidNamespace('x', 'x.com'); $xx_ns = fluidns('xx', 'xx.com', FluidNamespace::MODE_IMPLICIT); $nss = $xml->namespace($x_ns) ->namespace($xx_ns) ->namespaces(); $actual = $nss[$x_ns->id()]; $expected = $x_ns; \assert($actual === $expected, __($actual, $expected)); $actual = $nss[$xx_ns->id()]; $expected = $xx_ns; \assert($actual === $expected, __($actual, $expected)); }); it('should accept an id, an uri and an optional mode flag', function () { $xml = new FluidXml(); $nss = $xml->namespace('x', 'x.com') ->namespace('xx', 'xx.com', FluidNamespace::MODE_IMPLICIT) ->namespaces(); $actual = $nss['x']->uri(); $expected = 'x.com'; \assert($actual === $expected, __($actual, $expected)); $actual = $nss['x']->mode(); $expected = FluidNamespace::MODE_EXPLICIT; \assert($actual === $expected, __($actual, $expected)); $actual = $nss['xx']->uri(); $expected = 'xx.com'; \assert($actual === $expected, __($actual, $expected)); $actual = $nss['xx']->mode(); $expected = FluidNamespace::MODE_IMPLICIT; \assert($actual === $expected, __($actual, $expected)); }); it('should accept variable namespaces arguments', function () { $xml = new FluidXml(); $x_ns = new FluidNamespace('x', 'x.com'); $xx_ns = fluidns('xx', 'xx.com', FluidNamespace::MODE_IMPLICIT); $nss = $xml->namespace($x_ns, $xx_ns) ->namespaces(); $actual = $nss[$x_ns->id()]; $expected = $x_ns; \assert($actual === $expected, __($actual, $expected)); $actual = $nss[$xx_ns->id()]; $expected = $xx_ns; \assert($actual === $expected, __($actual, $expected)); }); }); describe('.query()', function () { it('should be fluid', function () { assert_is_fluid('query', '.'); }); it('should accept a query that return the root nodes of the document (XPath)', function () { $xml = new FluidXml(); $cx = $xml->query('/*'); $actual = $cx[0]->nodeName; $expected = 'doc'; \assert($actual === $expected, __($actual, $expected)); $xml->appendSibling('meta'); $cx = $xml->query('/*'); $actual = $cx[0]->nodeName; $expected = 'doc'; \assert($actual === $expected, __($actual, $expected)); $actual = $cx[1]->nodeName; $expected = 'meta'; \assert($actual === $expected, __($actual, $expected)); }); it('should accept a query that return the root nodes of the document (CSS)', function () { $xml = new FluidXml(); $cx = $xml->query(':root'); $actual = $cx[0]->nodeName; $expected = 'doc'; \assert($actual === $expected, __($actual, $expected)); $xml->appendSibling('meta'); $cx = $xml->query(':root'); $actual = $cx[0]->nodeName; $expected = 'doc'; \assert($actual === $expected, __($actual, $expected)); $actual = $cx[1]->nodeName; $expected = 'meta'; \assert($actual === $expected, __($actual, $expected)); }); it('should accept an array of queries (XPath)', function () { $xml = new FluidXml(); $xml->addChild('html', true) ->addChild(['head','body']) ->query(['//html', 'head', '//body']) ->setAttribute('lang', 'en'); $expected = "<doc>\n" . " <html lang=\"en\">\n" . " <head lang=\"en\"/>\n" . " <body lang=\"en\"/>\n" . " </html>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should accept an array of queries (XPath and CSS)', function () { $xml = new FluidXml(); $xml->addChild('html', true) ->addChild(['head','body']) ->query(['//html', 'head', '//body']) ->setAttribute('lang', 'en'); $expected = "<doc>\n" . " <html lang=\"en\">\n" . " <head lang=\"en\"/>\n" . " <body lang=\"en\"/>\n" . " </html>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should accept a variable number of queries (XPath and CSS)', function () { $xml = new FluidXml(); $xml->addChild('html', true) ->addChild(['head','body']) ->query('//html', 'head', '//body') ->setAttribute('lang', 'en'); $expected = "<doc>\n" . " <html lang=\"en\">\n" . " <head lang=\"en\"/>\n" . " <body lang=\"en\"/>\n" . " </html>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should support relative queries (XPath)', function () { $xml = new FluidXml(); $cx = $xml->addChild('html', true) ->addChild(['head','body']) ->query('./body'); $actual = $cx[0]->nodeName; $expected = 'body'; \assert($actual === $expected, __($actual, $expected)); $xml = new FluidXml(); $xml->addChild('html', true)->addChild(['head','body']); $cx = $xml->query('/doc/html')->query('./head'); $actual = $cx[0]->nodeName; $expected = 'head'; \assert($actual === $expected, __($actual, $expected)); }); it('should query the root of the document from a sub query (XPath)', function () { $xml = new FluidXml(); $xml->addChild('html', true) ->addChild(['head','body']); $cx = $xml->query('/doc/html/body') ->addChild('h1') ->query('/doc/html/head'); $actual = $cx[0]->nodeName; $expected = 'head'; \assert($actual === $expected, __($actual, $expected)); }); it('should query the root of the document from a sub query (CSS)', function () { $xml = new FluidXml(); $xml->addChild('html', true) ->addChild(['head','body']); $cx = $xml->query('body') ->addChild('h1') ->query(':root head'); $actual = $cx[0]->nodeName; $expected = 'head'; \assert($actual === $expected, __($actual, $expected)); }); it('should perform relative queries (XPath) ascending the DOM tree', function () { $xml = new FluidXml(); $xml->addChild('html', true) ->addChild(['head','body'], true) ->query('../body') ->addChild('h1') ->query('../..') ->addChild('extra'); $expected = "<doc>\n" . " <html>\n" . " <head/>\n" . " <body>\n" . " <h1/>\n" . " </body>\n" . " </html>\n" . " <extra/>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should query namespaced nodes (XPath)', function () { $xml = new FluidXml(); $x_ns = new FluidNamespace('x', 'x.com'); $xx_ns = fluidns('xx', 'xx.com', FluidNamespace::MODE_IMPLICIT); $xml->namespace($x_ns, $xx_ns); $xml->addChild('x:a', true) ->addChild('x:b', true) ->addChild('xx:c', true) ->addChild('xx:d', true) ->addChild('e', true) ->addChild('x:f', true) ->addChild('g'); $r = $xml->query('/doc/a'); $actual = $r->length(); $expected = 0; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('/doc/x:a'); $actual = $r[0]->nodeName; $expected = 'x:a'; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('/doc/x:a/x:b'); $actual = $r[0]->nodeName; $expected = 'x:b'; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('/doc/x:a/x:b/c'); $actual = $r->length(); $expected = 0; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('/doc/x:a/x:b/xx:c'); $actual = $r[0]->nodeName; $expected = 'c'; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('/doc/x:a/x:b/xx:c/xx:d'); $actual = $r[0]->nodeName; $expected = 'd'; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('/doc/x:a/x:b/xx:c/xx:d/e'); $actual = $r[0]->nodeName; $expected = 'e'; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('/doc/x:a/x:b/xx:c/xx:d/e/f'); $actual = $r->length(); $expected = 0; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('/doc/x:a/x:b/xx:c/xx:d/e/x:f'); $actual = $r[0]->nodeName; $expected = 'x:f'; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('/doc/x:a/x:b/xx:c/xx:d/e/x:f/g'); $actual = $r[0]->nodeName; $expected = 'g'; \assert($actual === $expected, __($actual, $expected)); }); it('should query namespaced nodes (CSS)', function () { $xml = new FluidXml(); $x_ns = new FluidNamespace('x', 'x.com'); $xx_ns = fluidns('xx', 'xx.com', FluidNamespace::MODE_IMPLICIT); $xml->namespace($x_ns, $xx_ns); $xml->addChild('x:a', true) ->addChild('x:b', true) ->addChild('xx:c', true) ->addChild('xx:d', true) ->addChild('e', true) ->addChild('x:f', true) ->addChild('g'); $r = $xml->query('a'); $actual = $r->length(); $expected = 0; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('x|a'); $actual = $r[0]->nodeName; $expected = 'x:a'; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('x|a > x|b'); $actual = $r[0]->nodeName; $expected = 'x:b'; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('x|a > x|b > c'); $actual = $r->length(); $expected = 0; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('x|a > x|b > xx|c'); $actual = $r[0]->nodeName; $expected = 'c'; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('x|a > x|b > xx|c > xx|d'); $actual = $r[0]->nodeName; $expected = 'd'; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('x|a > x|b > xx|c > xx|d > e'); $actual = $r[0]->nodeName; $expected = 'e'; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('x|a > x|b > xx|c > xx|d > e > f'); $actual = $r->length(); $expected = 0; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('x|a > x|b > xx|c > xx|d > e > x|f'); $actual = $r[0]->nodeName; $expected = 'x:f'; \assert($actual === $expected, __($actual, $expected)); $r = $xml->query('x|a > x|b > xx|c > xx|d > e > x|f > g'); $actual = $r[0]->nodeName; $expected = 'g'; \assert($actual === $expected, __($actual, $expected)); }); }); describe('.__invoke()', function () { it('should be fluid', function () { assert_is_fluid('__invoke', '/*'); }); it('should behave like .query()', function () { $xml = new FluidXml(); $actual = $xml('/*'); $expected = $xml->query('/*'); \assert($actual == $expected, __($actual, $expected)); }); }); describe('.each()', function () { it('should be fluid', function () { assert_is_fluid('each', function (){}); }); it('should iterate the nodes inside the context', function () { $xml = new FluidXml(); $xml->each(function ($i, $n) { assert_is_a($this, FluidContext::class); assert_is_a($n, \DOMNode::class); $actual = $i; $expected = 0; \assert($actual === $expected, __($actual, $expected)); }); function eachassert($cx, $i, $n) { assert_is_a($cx, FluidContext::class); assert_is_a($n, \DOMNode::class); $actual = $i; $expected = 0; \assert($actual === $expected, __($actual, $expected)); } $xml->each('eachassert'); $xml->addChild('child1') ->addChild('child2'); $nodes = []; $index = 0; $xml->query('/doc/*') ->each(function ($i, $n) use (&$nodes, &$index) { $idx = $i + 1; $this->setText($n->nodeName . $idx); $nodes[] = $n; $actual = $i; $expected = $index; \assert($actual === $expected, __($actual, $expected)); ++$index; }); $actual = $nodes; $expected = $xml->query('/doc/*')->array(); \assert($actual === $expected, __($actual, $expected)); $expected = "<doc>\n" . " <child1>child11</child1>\n" . " <child2>child22</child2>\n" . "</doc>"; assert_equal_xml($xml, $expected); $xml = new FluidXml(); $xml->addChild('child1') ->addChild('child2'); function eachsettext($cx, $i, $n) { $idx = $i + 1; $cx->setText($n->nodeName . $idx); } $xml->query('/doc/*') ->each('eachsettext'); $expected = "<doc>\n" . " <child1>child11</child1>\n" . " <child2>child22</child2>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); }); describe('.map()', function () { it('should map over the nodes inside the context', function () { $xml = new FluidXml(); $xml->map(function ($i, $n) { assert_is_a($this, FluidContext::class); assert_is_a($n, \DOMNode::class); $actual = $i; $expected = 0; \assert($actual === $expected, __($actual, $expected)); }); function mapassert($cx, $i, $n) { assert_is_a($cx, FluidContext::class); assert_is_a($n, \DOMNode::class); $actual = $i; $expected = 0; \assert($actual === $expected, __($actual, $expected)); } $xml->map('mapassert'); $xml->addChild(['child1' => 'child1']) ->addChild(['child2' => 'child2']); $actual = $xml->query('/doc/*') ->map(function ($i, $n) { $idx = $i + 1; return $n->nodeValue . $idx; }); $expected = ['child11', 'child22']; \assert($actual === $expected, __($actual, $expected)); function mapfn($cx, $i, $n) { $idx = $i + 1; return $n->nodeValue . $idx; } $actual = $xml->query('/doc/*')->map('mapfn'); \assert($actual === $expected, __($actual, $expected)); }); }); describe('.filter()', function () { it('should be fluid', function () { assert_is_fluid('filter', function (){}); }); it('should filter the nodes inside the context', function () { $xml = new FluidXml(); $xml->filter(function ($i, $n) { assert_is_a($this, FluidContext::class); assert_is_a($n, \DOMNode::class); $actual = $i; $expected = 0; \assert($actual === $expected, __($actual, $expected)); }); function filterassert($cx, $i, $n) { assert_is_a($cx, FluidContext::class); assert_is_a($n, \DOMNode::class); $actual = $i; $expected = 0; \assert($actual === $expected, __($actual, $expected)); } $xml->each('filterassert'); $xml->times(4)->addChild('child'); $index = 0; $children = $xml->query('//child'); $cx = $children->filter(function ($i, $n) use (&$index) { $actual = $i; $expected = $index; \assert($actual === $expected, __($actual, $expected)); ++$index; if ($i === 0) { return true; } if (($i % 2) === 0) { return false; } }); $actual = $cx->array(); $expected = [ $children[0], $children[1], $children[3] ]; \assert($actual === $expected, __($actual, $expected)); $cx->setText('not filtered'); $expected = "<doc>\n" . " <child>not filtered</child>\n" . " <child>not filtered</child>\n" . " <child/>\n" . " <child>not filtered</child>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); }); describe('.times()', function () { it('should be fluid', function () { assert_is_a((new FluidXml())->times(4), FluidRepeater::class); assert_is_fluid('times', 4, function () {}); }); it('should repeat the following one method call (if no callable is passed)', function () { $xml = new FluidXml(); $xml->times(2) ->add('child') ->add('lastchild'); $expected = "<doc>\n" . " <child/>\n" . " <child/>\n" . " <lastchild/>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should switch context', function () { $xml = new FluidXml(); $xml->times(2) ->add('child', true) ->add('subchild'); $expected = "<doc>\n" . " <child>\n" . " <subchild/>\n" . " </child>\n" . " <child>\n" . " <subchild/>\n" . " </child>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should repeat a closure bound to $this of the context', function () { $xml = new FluidXml(); $xml->add('parent', true) ->times(2, function ($i) { $this->add("child{$i}"); }); $expected = "<doc>\n" . " <parent>\n" . " <child0/>\n" . " <child1/>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should repeat a callable', function () { $xml = new FluidXml(); function addchild($parent, $i) { $parent->add("child{$i}"); } $xml->add('parent', true) ->times(2, 'addchild'); $expected = "<doc>\n" . " <parent>\n" . " <child0/>\n" . " <child1/>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should repeat a callable without repeating the following method call', function () { $xml = new FluidXml(); $xml->add('parent', true) ->times(2, function ($i) { $this->add("child{$i}"); }) ->add('lastchild'); $expected = "<doc>\n" . " <parent>\n" . " <child0/>\n" . " <child1/>\n" . " <lastchild/>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); }); describe('.addChild()', function () { it('should be fluid', function () { assert_is_fluid('addChild', 'a'); }); it('should add a child using the argument syntax', function () { $xml = new FluidXml(); $xml->addChild('child1') ->addChild('parent', true) ->addChild('child2'); $expected = "<doc>\n" . " <child1/>\n" . " <parent>\n" . " <child2/>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add a child using the array syntax', function () { $xml = new FluidXml(); $xml->addChild(['child1']) ->addChild(['parent'], true) ->addChild(['child2']); $expected = "<doc>\n" . " <child1/>\n" . " <parent>\n" . " <child2/>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add a child with a string value using the argument syntax', function () { $xml = new FluidXml(); $xml->addChild('child1', 'value1') ->addChild('parent', true) ->addChild('child2', 'value2'); $expected = "<doc>\n" . " <child1>value1</child1>\n" . " <parent>\n" . " <child2>value2</child2>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add a child with a string value using the array syntax', function () { $xml = new FluidXml(); $xml->addChild(['child1' => 'value1']) ->addChild('parent', true) ->addChild(['child2' => 'value2']); $expected = "<doc>\n" . " <child1>value1</child1>\n" . " <parent>\n" . " <child2>value2</child2>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add a child with an empty string value using the argument syntax', function () { $xml = new FluidXml(); $xml->addChild('child1', '') ->addChild('parent', true) ->addChild('child2', ''); $expected = "<doc>\n" . " <child1/>\n" . " <parent>\n" . " <child2/>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add a child with an empty string value using the array syntax', function () { $xml = new FluidXml(); $xml->addChild(['child1' => '']) ->addChild('parent', true) ->addChild(['child2' => '']); $expected = "<doc>\n" . " <child1/>\n" . " <parent>\n" . " <child2/>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add a child with a null value using the argument syntax', function () { $xml = new FluidXml(); $xml->addChild('child1', null) ->addChild('parent', true) ->addChild('child2', null); $expected = "<doc>\n" . " <child1/>\n" . " <parent>\n" . " <child2/>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add a child with a null value using the array syntax', function () { $xml = new FluidXml(); $xml->addChild(['child1' => null]) ->addChild('parent', true) ->addChild(['child2' => null]); $expected = "<doc>\n" . " <child1/>\n" . " <parent>\n" . " <child2/>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add a child with an integer value using the argument syntax', function () { $xml = new FluidXml(); $xml->addChild('child1', 1) ->addChild('parent', true) ->addChild('child2', 1); $expected = "<doc>\n" . " <child1>1</child1>\n" . " <parent>\n" . " <child2>1</child2>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add a child with an integer value using the array syntax', function () { $xml = new FluidXml(); $xml->addChild(['child1' => 1]) ->addChild('parent', true) ->addChild(['child2' => 1]); $expected = "<doc>\n" . " <child1>1</child1>\n" . " <parent>\n" . " <child2>1</child2>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add a child with a 0 value using the argument syntax', function () { $xml = new FluidXml(); $xml->addChild('child1', 0) ->addChild('parent', true) ->addChild('child2', 0); $expected = "<doc>\n" . " <child1>0</child1>\n" . " <parent>\n" . " <child2>0</child2>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add a child with a 0 value using the array syntax', function () { $xml = new FluidXml(); $xml->addChild(['child1' => 0]) ->addChild('parent', true) ->addChild(['child2' => 0]); $expected = "<doc>\n" . " <child1>0</child1>\n" . " <parent>\n" . " <child2>0</child2>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add many children with and without a value', function () { $xml = new FluidXml(); $xml->addChild(['child1', 'child2', 'child3' => 'value3', 'child4' => 'value4']) ->addChild('parent', true) ->addChild(['child5', 'child6', 'child7' => 'value7', 'child8' => 'value8']); $expected = "<doc>\n" . " <child1/>\n" . " <child2/>\n" . " <child3>value3</child3>\n" . " <child4>value4</child4>\n" . " <parent>\n" . " <child5/>\n" . " <child6/>\n" . " <child7>value7</child7>\n" . " <child8>value8</child8>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add many children of the same name with and without a value', function () { $xml = new FluidXml(); $xml->addChild(['child', ['child'], ['child' => 'value1'], ['child' => 'value2']]) ->addChild('parent', true) ->addChild(['child', ['child'], ['child' => 'value3'], ['child' => 'value4']]); $expected = "<doc>\n" . " <child/>\n" . " <child/>\n" . " <child>value1</child>\n" . " <child>value2</child>\n" . " <parent>\n" . " <child/>\n" . " <child/>\n" . " <child>value3</child>\n" . " <child>value4</child>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add many children with nested arrays', function () { $xml = new FluidXml(); $xml->addChild(['child1'=>['child11'=>['child111', 'child112'=>'value112'], 'child12'=>'value12'], 'child2'=>['child21', 'child22'=>['child221', 'child222']]]) ->addChild('parent', true) ->addChild(['child3'=>['child31'=>['child311', 'child312'=>'value312'], 'child32'=>'value32'], 'child4'=>['child41', 'child42'=>['child421', 'child422']]]); $expected = <<<EOF <doc> <child1> <child11> <child111/> <child112>value112</child112> </child11> <child12>value12</child12> </child1> <child2> <child21/> <child22> <child221/> <child222/> </child22> </child2> <parent> <child3> <child31> <child311/> <child312>value312</child312> </child31> <child32>value32</child32> </child3> <child4> <child41/> <child42> <child421/> <child422/> </child42> </child4> </parent> </doc> EOF; assert_equal_xml($xml, $expected); }); it('should add a child with some attributes', function () { $xml = new FluidXml(); $xml->addChild('child1', ['class' => 'Class attr', 'id' => 'Id attr1']) ->addChild('parent', true) ->addChild('child2', ['class' => 'Class attr', 'id' => 'Id attr2']); $expected = "<doc>\n" . " <child1 class=\"Class attr\" id=\"Id attr1\"/>\n" . " <parent>\n" . " <child2 class=\"Class attr\" id=\"Id attr2\"/>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add many children with some attributes', function () { $xml = new FluidXml(); $xml->addChild(['child1', 'child2'], ['class' => 'Class attr', 'id' => 'Id attr1']) ->addChild('parent', true) ->addChild(['child3', 'child4'], ['class' => 'Class attr', 'id' => 'Id attr2']); $expected = "<doc>\n" . " <child1 class=\"Class attr\" id=\"Id attr1\"/>\n" . " <child2 class=\"Class attr\" id=\"Id attr1\"/>\n" . " <parent>\n" . " <child3 class=\"Class attr\" id=\"Id attr2\"/>\n" . " <child4 class=\"Class attr\" id=\"Id attr2\"/>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add children with some attributes and text using the @ syntax', function () { $xml = new FluidXml(); $attrs = [ '@class' => 'Class attr', '@' => 'Text content', '@id' => 'Id attr' ]; $xml->addChild(['child1' => $attrs ]) ->addChild(['child2' => $attrs ], true) ->addChild(['child3' => $attrs ]); $expected = "<doc>\n" . " <child1 class=\"Class attr\" id=\"Id attr\">Text content</child1>\n" . " <child2 class=\"Class attr\" id=\"Id attr\">" . "Text content" . "<child3 class=\"Class attr\" id=\"Id attr\">Text content</child3>" . "</child2>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should switch context', function () { $xml = new FluidXml(); $actual = $xml->addChild('child', true); assert_is_a($actual, FluidContext::class); $actual = $xml->addChild('child', 'value', true); assert_is_a($actual, FluidContext::class); $actual = $xml->addChild(['child1', 'child2'], true); assert_is_a($actual, FluidContext::class); $actual = $xml->addChild(['child1' => 'value1', 'child2' => 'value2'], true); assert_is_a($actual, FluidContext::class); $actual = $xml->addChild('child', ['attr' => 'value'], true); assert_is_a($actual, FluidContext::class); $actual = $xml->addChild(['child1', 'child2'], ['attr' => 'value'], true); assert_is_a($actual, FluidContext::class); }); it('should add namespaced children', function () { $xml = new FluidXml(); $xml->namespace(new FluidNamespace('x', 'x.com')); $xml->namespace(fluidns('xx', 'xx.com', FluidNamespace::MODE_IMPLICIT)); $xml->addChild('x:xTag1', true) ->addChild('x:xTag2'); $xml->addChild('xx:xxTag1', true) ->addChild('xx:xxTag2') ->addChild('tag3'); $expected = "<doc>\n" . " <x:xTag1 xmlns:x=\"x.com\">\n" . " <x:xTag2/>\n" . " </x:xTag1>\n" . " <xxTag1 xmlns=\"xx.com\">\n" . " <xxTag2/>\n" . " <tag3/>\n" . " </xxTag1>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); $doc = "<doc>\n" . " <parent>content</parent>\n" . "</doc>"; $dom = new \DOMDocument(); $dom->loadXML($doc); it('should fill the document with an XML string', function () { $xml = new FluidXml(null); $xml->addChild('<root/>'); $expected = "<root/>"; assert_equal_xml($xml, $expected); }); it('should fill the document with an XML string with multiple root nodes', function () { $xml = new FluidXml(null); $xml->addChild('<root1/><root2/>'); $expected = "<root1/>\n" . "<root2/>"; assert_equal_xml($xml, $expected); }); it('should add an XML string with multiple root nodes', function () { $xml = new FluidXml(); $xml->addChild('<child1/><child2/>'); $expected = "<doc>\n" . " <child1/>\n" . " <child2/>\n" . "</doc>"; assert_equal_xml($xml, $expected); $xml = new FluidXml(); $xml->addChild('parent', true) ->addChild('<child1/><child2/>'); $expected = "<doc>\n" . " <parent>\n" . " <child1/>\n" . " <child2/>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add a DOMDocument', function () use ($doc) { $dom = new DOMDocument(); $dom->loadXML('<parent>content</parent>'); $xml = new FluidXml(); $xml->addChild($dom); $expected = $doc; assert_equal_xml($xml, $expected); }); it('should add a DOMNode', function () use ($doc, $dom) { $xp = new \DOMXPath($dom); $nodes = $xp->query('/doc/parent'); $xml = new FluidXml(); $xml->addChild($nodes[0]); $expected = $doc; assert_equal_xml($xml, $expected); }); it('should add a DOMNodeList', function () use ($doc, $dom) { $xp = new \DOMXPath($dom); $nodes = $xp->query('/doc/parent'); $xml = new FluidXml(); $xml->addChild($nodes); $expected = $doc; assert_equal_xml($xml, $expected); }); it('should add a SimpleXMLElement', function () use ($doc, $dom) { $sxml = \simplexml_import_dom($dom); $xml = new FluidXml(); $xml->addChild($sxml->children()); $expected = $doc; assert_equal_xml($xml, $expected); }); it('should add a FluidXml', function () use ($doc, $dom) { $nodes = $dom->documentElement->childNodes; $fxml = new FluidXml($nodes); $xml = new FluidXml(); $xml->addChild($fxml); $expected = $doc; assert_equal_xml($xml, $expected); }); it('should add a FluidContext', function () use ($doc, $dom) { $fxml = (new FluidXml($dom))->query('/doc/parent'); $xml = new FluidXml(); $xml->addChild($fxml); $expected = $doc; assert_equal_xml($xml, $expected); }); it('should add many instances', function () use ($doc, $dom) { $fxml = (new FluidXml($dom))->query('/doc/parent'); $xml = new FluidXml(); $xml->addChild([ $fxml, 'imported' => $fxml ]); $expected = "<doc>\n" . " <parent>content</parent>\n" . " <imported>\n" . " <parent>content</parent>\n" . " </imported>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should throw for not supported input', function () { $xml = new FluidXml(); try { $xml->addChild(0); } catch (\Exception $e) { $actual = $e; } assert_is_a($actual, \Exception::class); }); }); describe('.add()', function () { it('should be fluid', function () { assert_is_fluid('add', 'a'); }); it('should behave like .addChild()', function () { $xml = new FluidXml(); $xml->addChild('parent', true) ->addChild(['child1', 'child2'], ['class'=>'child']); $alias = new FluidXml(); $alias->add('parent', true) ->add(['child1', 'child2'], ['class'=>'child']); $actual = $xml->xml(); $expected = $alias->xml(); \assert($actual === $expected, __($actual, $expected)); }); }); describe('.prependSibling()', function () { it('should be fluid', function () { assert_is_fluid('prependSibling', 'a'); }); it('should add more than one root node to a document with one root node', function () { $xml = new FluidXml(); $xml->prependSibling('meta'); $xml->prependSibling('extra'); $cx = $xml->query('/*'); $actual = $cx[0]->nodeName; $expected = 'extra'; \assert($actual === $expected, __($actual, $expected)); $actual = $cx[1]->nodeName; $expected = 'meta'; \assert($actual === $expected, __($actual, $expected)); $actual = $cx[2]->nodeName; $expected = 'doc'; \assert($actual === $expected, __($actual, $expected)); }); it('should add more than one root node to a document with no root node', function () { $xml = new FluidXml(null); $xml->prependSibling('meta'); $xml->prependSibling('extra'); $cx = $xml->query('/*'); $actual = $cx[0]->nodeName; $expected = 'extra'; \assert($actual === $expected, __($actual, $expected)); $actual = $cx[1]->nodeName; $expected = 'meta'; \assert($actual === $expected, __($actual, $expected)); }); it('should add a sibling node before a node', function () { $xml = new FluidXml(); $xml->addChild('parent', true) ->prependSibling('sibling1') ->prependSibling('sibling2'); $expected = "<doc>\n" . " <sibling1/>\n" . " <sibling2/>\n" . " <parent/>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add an XML document instance before a node', function () { $dom = new DOMDocument(); $dom->loadXML('<parent>content</parent>'); $xml = new FluidXml(); $xml->prependSibling($dom); $expected = "<parent>content</parent>\n" . "<doc/>"; assert_equal_xml($xml, $expected); $xml = new FluidXml(); $xml->addChild('sibling', true) ->prependSibling($dom); $expected = "<doc>\n" . " <parent>content</parent>\n" . " <sibling/>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); }); describe('.prepend()', function () { it('should be fluid', function () { assert_is_fluid('prepend', 'a'); }); it('should behave like .prependSibling()', function () { $xml = new FluidXml(); $xml->prependSibling('sibling1', true) ->prependSibling(['sibling2', 'sibling3'], ['class'=>'sibling']); $alias = new FluidXml(); $alias->prepend('sibling1', true) ->prepend(['sibling2', 'sibling3'], ['class'=>'sibling']); $actual = $xml->xml(); $expected = $alias->xml(); \assert($actual === $expected, __($actual, $expected)); }); }); describe('.appendSibling()', function () { it('should be fluid', function () { assert_is_fluid('appendSibling', 'a'); }); it('should add more than one root node to a document with one root node', function () { $xml = new FluidXml(); $xml->appendSibling('meta'); $xml->appendSibling('extra'); $cx = $xml->query('/*'); $actual = $cx[0]->nodeName; $expected = 'doc'; \assert($actual === $expected, __($actual, $expected)); $actual = $cx[1]->nodeName; $expected = 'extra'; \assert($actual === $expected, __($actual, $expected)); $actual = $cx[2]->nodeName; $expected = 'meta'; \assert($actual === $expected, __($actual, $expected)); }); it('should add more than one root node to a document with no root node', function () { $xml = new FluidXml(null); $xml->appendSibling('meta'); $xml->appendSibling('extra'); $cx = $xml->query('/*'); $actual = $cx[0]->nodeName; $expected = 'meta'; \assert($actual === $expected, __($actual, $expected)); $actual = $cx[1]->nodeName; $expected = 'extra'; \assert($actual === $expected, __($actual, $expected)); }); it('should add a sibling node after a node', function () { $xml = new FluidXml(); $xml->addChild('parent', true) ->appendSibling('sibling1') ->appendSibling('sibling2'); $expected = "<doc>\n" . " <parent/>\n" . " <sibling2/>\n" . " <sibling1/>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add an XML document instance after a node', function () { $dom = new DOMDocument(); $dom->loadXML('<parent>content</parent>'); $xml = new FluidXml(); $xml->appendSibling($dom); $expected = "<doc/>\n" . "<parent>content</parent>"; assert_equal_xml($xml, $expected); $xml = new FluidXml(); $xml->addChild('sibling', true) ->appendSibling($dom); $expected = "<doc>\n" . " <sibling/>\n" . " <parent>content</parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); }); describe('.append()', function () { it('should be fluid', function () { assert_is_fluid('append', 'a'); }); it('should behave like .appendSibling()', function () { $xml = new FluidXml(); $xml->appendSibling('sibling1', true) ->appendSibling(['sibling2', 'sibling3'], ['class'=>'sibling']); $alias = new FluidXml(); $alias->append('sibling1', true) ->append(['sibling2', 'sibling3'], ['class'=>'sibling']); $actual = $xml->xml(); $expected = $alias->xml(); \assert($actual === $expected, __($actual, $expected)); }); }); describe('.setAttribute()', function () { it('should be fluid', function () { assert_is_fluid('setAttribute', 'a', 'b'); }); it('should set the attributes of the root node', function () { $xml = new FluidXml(); $xml->setAttribute('attr1', 'Attr1 Value') ->setAttribute('attr2', 'Attr2 Value'); $expected = "<doc attr1=\"Attr1 Value\" attr2=\"Attr2 Value\"/>"; assert_equal_xml($xml, $expected); $xml = new FluidXml(); $xml->setAttribute(['attr1' => 'Attr1 Value', 'attr2' => 'Attr2 Value']); $expected = "<doc attr1=\"Attr1 Value\" attr2=\"Attr2 Value\"/>"; assert_equal_xml($xml, $expected); }); it('should change the attributes of the root node', function () { $xml = new FluidXml(); $xml->setAttribute('attr1', 'Attr1 Value') ->setAttribute('attr2', 'Attr2 Value'); $xml->setAttribute('attr2', 'Attr2 New Value'); $expected = "<doc attr1=\"Attr1 Value\" attr2=\"Attr2 New Value\"/>"; assert_equal_xml($xml, $expected); $xml->setAttribute('attr1', 'Attr1 New Value'); $expected = "<doc attr1=\"Attr1 New Value\" attr2=\"Attr2 New Value\"/>"; assert_equal_xml($xml, $expected); }); it('should set the attributes of a node', function () { $xml = new FluidXml(); $xml->addChild('child', true) ->setAttribute('attr1', 'Attr1 Value') ->setAttribute('attr2', 'Attr2 Value'); $expected = "<doc>\n" . " <child attr1=\"Attr1 Value\" attr2=\"Attr2 Value\"/>\n" . "</doc>"; assert_equal_xml($xml, $expected); $xml = new FluidXml(); $xml->addChild('child', true) ->setAttribute(['attr1' => 'Attr1 Value', 'attr2' => 'Attr2 Value']); $expected = "<doc>\n" . " <child attr1=\"Attr1 Value\" attr2=\"Attr2 Value\"/>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should set the attributes, without values, of a node', function () { $xml = new FluidXml(); $xml->addChild('child', true) ->setAttribute('attr1') ->setAttribute('attr2'); $expected = "<doc>\n" . " <child attr1=\"\" attr2=\"\"/>\n" . "</doc>"; assert_equal_xml($xml, $expected); $xml = new FluidXml(); $xml->addChild('child', true) ->setAttribute(['attr1', 'attr2']); $expected = "<doc>\n" . " <child attr1=\"\" attr2=\"\"/>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should change the attributes of a node', function () { $xml = new FluidXml(); $xml->addChild('child', true) ->setAttribute('attr1', 'Attr1 Value') ->setAttribute('attr2', 'Attr2 Value') ->setAttribute('attr2', 'Attr2 New Value'); $expected = "<doc>\n" . " <child attr1=\"Attr1 Value\" attr2=\"Attr2 New Value\"/>\n" . "</doc>"; assert_equal_xml($xml, $expected); $xml = new FluidXml(); $xml->addChild('child', true) ->setAttribute(['attr1' => 'Attr1 Value', 'attr2' => 'Attr2 Value']) ->setAttribute('attr1', 'Attr1 New Value'); $expected = "<doc>\n" . " <child attr1=\"Attr1 New Value\" attr2=\"Attr2 Value\"/>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); }); describe('.attr()', function () { it('should be fluid', function () { assert_is_fluid('attr', 'a', 'b'); }); it('should behave like .setAttribute()', function () { $xml = new FluidXml(); $xml->setAttribute('attr1', 'Value 1') ->setAttribute('attr2') ->setAttribute(['attr3' => 'Value 3', 'attr4' => 'Value 4']) ->setAttribute(['attr5', 'attr6']) ->addChild('child', true) ->setAttribute('attr1', 'Value 1') ->setAttribute('attr2') ->setAttribute(['attr3' => 'Value 3', 'attr4' => 'Value 4']) ->setAttribute(['attr5', 'attr6']); $alias = new FluidXml(); $alias->attr('attr1', 'Value 1') ->attr('attr2') ->attr(['attr3' => 'Value 3', 'attr4' => 'Value 4']) ->attr(['attr5', 'attr6']) ->addChild('child', true) ->attr('attr1', 'Value 1') ->attr('attr2') ->attr(['attr3' => 'Value 3', 'attr4' => 'Value 4']) ->attr(['attr5', 'attr6']); $actual = $xml->xml(); $expected = $alias->xml(); \assert($actual === $expected, __($actual, $expected)); }); }); describe('.setText()', function () { it('should be fluid', function () { assert_is_fluid('setText', 'a'); }); it('should set/change the text of the root node', function () { $xml = new FluidXml(); $xml->setText('First Text') ->setText('Second Text'); $expected = "<doc>Second Text</doc>"; assert_equal_xml($xml, $expected); }); it('should set/change the text of a node', function () { $xml = new FluidXml(); $cx = $xml->addChild('p', true); $cx->setText('First Text') ->setText('Second Text'); $expected = "<doc>\n" . " <p>Second Text</p>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); }); describe('.text()', function () { it('should be fluid', function () { assert_is_fluid('text', 'a'); }); it('should behave like .setText()', function () { $xml = new FluidXml(); $xml->setText('Text1') ->addChild('child', true) ->setText('Text2'); $alias = new FluidXml(); $alias->text('Text1') ->addChild('child', true) ->text('Text2'); $actual = $xml->xml(); $expected = $alias->xml(); \assert($actual === $expected, __($actual, $expected)); }); }); describe('.addText()', function () { it('should be fluid', function () { assert_is_fluid('addText', 'a'); }); it('should add text to the root node', function () { $xml = new FluidXml(); $xml->addText('First Line') ->addText('Second Line'); $expected = "<doc>First LineSecond Line</doc>"; assert_equal_xml($xml, $expected); }); it('should add text to a node', function () { $xml = new FluidXml(); $cx = $xml->addChild('p', true); $cx->addText('First Line') ->addText('Second Line'); $expected = "<doc>\n" . " <p>First LineSecond Line</p>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); }); describe('.setCdata()', function () { it('should be fluid', function () { assert_is_fluid('setCdata', 'a'); }); it('should set/change the CDATA of the root node', function () { $xml = new FluidXml(); $xml->setCdata('First Data') ->setCdata('Second Data'); $expected = "<doc><![CDATA[Second Data]]></doc>"; assert_equal_xml($xml, $expected); }); it('should set/change the CDATA of a node', function () { $xml = new FluidXml(); $cx = $xml->addChild('p', true); $cx->setCdata('First Data') ->setCdata('Second Data'); $expected = "<doc>\n" . " <p><![CDATA[Second Data]]></p>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); }); describe('.cdata()', function () { it('should be fluid', function () { assert_is_fluid('cdata', 'a'); }); it('should behave like .setCdata()', function () { $xml = new FluidXml(); $xml->setCdata('Text1') ->addChild('child', true) ->setCdata('Text2'); $alias = new FluidXml(); $alias->cdata('Text1') ->addChild('child', true) ->cdata('Text2'); $actual = $xml->xml(); $expected = $alias->xml(); \assert($actual === $expected, __($actual, $expected)); }); }); describe('.addCdata()', function () { it('should be fluid', function () { assert_is_fluid('addCdata', 'a'); }); it('should add CDATA to the root node', function () { $xml = new FluidXml(); $xml->addCdata('// <, > are characters that should be escaped in a XML context.') ->addCdata('// Even & is a characters that should be escaped in a XML context.'); $expected = "<doc>" . "<![CDATA[// <, > are characters that should be escaped in a XML context.]]>" . "<![CDATA[// Even & is a characters that should be escaped in a XML context.]]>" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add CDATA to a node', function () { $xml = new FluidXml(); $cx = $xml->addChild('pre', true); $cx->addCdata('// <, > are characters that should be escaped in a XML context.') ->addCdata('// Even & is a characters that should be escaped in a XML context.'); $expected = "<doc>\n" . " <pre>" . "<![CDATA[// <, > are characters that should be escaped in a XML context.]]>" . "<![CDATA[// Even & is a characters that should be escaped in a XML context.]]>" . "</pre>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); }); describe('.setComment()', function () { it('should be fluid', function () { assert_is_fluid('setComment', 'a'); }); it('should set/change the comment of the root node', function () { $xml = new FluidXml(); $xml->setComment('First') ->setComment('Second'); $expected = "<doc><!--Second--></doc>"; assert_equal_xml($xml, $expected); }); it('should set/change the comment of a node', function () { $xml = new FluidXml(); $cx = $xml->addChild('p', true); $cx->setComment('First') ->setComment('Second'); $expected = "<doc>\n" . " <p><!--Second--></p>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); }); describe('.comment()', function () { it('should be fluid', function () { assert_is_fluid('comment', 'a'); }); it('should behave like .setComment()', function () { $xml = new FluidXml(); $xml->setComment('Text1') ->addChild('child', true) ->setComment('Text2'); $alias = new FluidXml(); $alias->comment('Text1') ->addChild('child', true) ->comment('Text2'); $actual = $xml->xml(); $expected = $alias->xml(); \assert($actual === $expected, __($actual, $expected)); }); }); describe('.addComment()', function () { it('should be fluid', function () { assert_is_fluid('addComment', 'a'); }); it('should add comments to the root node', function () { $xml = new FluidXml(); $xml->addComment('First') ->addComment('Second'); $expected = "<doc>\n" . " <!--First-->\n" . " <!--Second-->\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should add comments to a node', function () { $xml = new FluidXml(); $cx = $xml->addChild('pre', true); $cx->addComment('First') ->addComment('Second'); $expected = "<doc>\n" . " <pre>\n" . " <!--First-->\n" . " <!--Second-->\n" . " </pre>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); }); describe('.remove()', function () { $expected = "<doc>\n" . " <parent/>\n" . "</doc>"; $new_doc = function () { $xml = new FluidXml(); $xml->addChild('parent', true) ->addChild(['child1', 'child2'], ['class'=>'removable']); return $xml; }; it('should be fluid', function () { assert_is_fluid('remove', 'a'); }); it('should remove the root node', function () use ($new_doc) { $xml = $new_doc(); $xml->remove(); assert_equal_xml($xml, ''); }); it('should remove the results of the previous query', function () use ($new_doc, $expected) { $xml = $new_doc(); $xml->query('//*[@class="removable"]')->remove(); assert_equal_xml($xml, $expected); }); it('should remove the absolute and relative targets of a query (XPath)', function () use ($new_doc, $expected) { $xml = $new_doc(); $xml->remove('//*[@class="removable"]'); assert_equal_xml($xml, $expected); $xml = $new_doc(); $xml->query('/doc')->remove('//*[@class="removable"]'); assert_equal_xml($xml, $expected); $xml = $new_doc(); $xml->query('/doc/parent')->remove('./*[@class="removable"]'); assert_equal_xml($xml, $expected); }); it('should remove the absolute and relative targets of a query (CSS)', function () use ($new_doc, $expected) { $xml = $new_doc(); $xml->remove('.removable'); assert_equal_xml($xml, $expected); $xml = $new_doc(); $xml->query('/doc')->remove(':root .removable'); assert_equal_xml($xml, $expected); $xml = $new_doc(); $xml->query('/doc/parent')->remove('.removable'); assert_equal_xml($xml, $expected); }); it('should remove the absolute and relative targets of an array of queries (XPath and CSS)', function () use ($new_doc, $expected) { $xml = $new_doc(); $xml->remove(['//child1', ':root child2']); assert_equal_xml($xml, $expected); $xml = $new_doc(); $xml->query('/doc')->remove(['//child1', ':root child2']); assert_equal_xml($xml, $expected); $xml = $new_doc(); $xml->query('/doc/parent')->remove(['./child1', 'child2']); assert_equal_xml($xml, $expected); }); it('should remove the absolute and relative targets of a variable list of queries (XPath and CSS)', function () use ($new_doc, $expected) { $xml = $new_doc(); $xml->remove('//child1', ':root child2'); assert_equal_xml($xml, $expected); $xml = $new_doc(); $xml->query('/doc')->remove('//child1', ':root child2'); assert_equal_xml($xml, $expected); $xml = $new_doc(); $xml->query('/doc/parent')->remove('./child1', 'child2'); assert_equal_xml($xml, $expected); }); }); describe('.dom()', function () { it('should return the associated DOMDocument instace', function () { $xml = new FluidXml(); $actual = $xml->dom(); assert_is_a($actual, \DOMDocument::class); $actual = $xml->query('/*')->dom(); assert_is_a($actual, \DOMDocument::class); }); }); describe('.xml()', function () { it('should return the document as XML string', function () { $xml = new FluidXml(); $xml->addChild('parent', true) ->addChild('child', 'content'); $expected = "<doc>\n" . " <parent>\n" . " <child>content</child>\n" . " </parent>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); it('should return the document as XML string without the XML headers (declaration and stylesheet)', function () { $xml = new FluidXml('doc', ['stylesheet' => 'x.com/style.xsl']); $xml->addChild('parent', true) ->addChild('child', 'content'); $actual = $xml->xml(true); $expected = "<doc>\n" . " <parent>\n" . " <child>content</child>\n" . " </parent>\n" . "</doc>"; \assert($actual === $expected, __($actual, $expected)); }); it('should return a node and the descendants as XML string', function () { $xml = new FluidXml(); $xml->addChild('parent', true) ->addText('parent content') ->addChild('child', 'content'); $actual = $xml->query('//parent')->xml(); $expected = "<parent>parent content<child>content</child></parent>"; \assert($actual === $expected, __($actual, $expected)); $xml = new FluidXml(); $xml->addChild('parent', true) ->addChild('child', 'content1') ->addChild('child', 'content2'); $actual = $xml->query('//child')->xml(); $expected = "<child>content1</child>\n" . "<child>content2</child>"; \assert($actual === $expected, __($actual, $expected)); }); }); describe('.__toString()', function () { it('should behave like .xml()', function () { $xml = new FluidXml(); $cx = $xml->addChild('parent', true) ->addChild(['child1', 'child2']); $actual = \trim("$xml"); $expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" . "<doc>\n" . " <parent>\n" . " <child1/>\n" . " <child2/>\n" . " </parent>\n" . "</doc>"; \assert($actual === $expected, __($actual, $expected)); $actual = "$cx"; $expected = "<parent>\n" . " <child1/>\n" . " <child2/>\n" . "</parent>"; \assert($actual === $expected, __($actual, $expected)); }); }); describe('.html()', function () { it('should return the document as valid HTML 5 string', function () { $xml = new FluidXml([ 'html' => [ 'body' => [ 'input', // Void. 'div', /* Not void. */ ] ] ]); $actual = $xml->html(); $expected = "<!DOCTYPE html>\n" . "<html>\n" . " <body>\n" . " <input/>\n" . " <div></div>\n" . " </body>\n" . "</html>"; \assert($actual === $expected, __($actual, $expected)); }); it('should return the document as valid HTML 5 string without the doctype', function () { $xml = new FluidXml([ 'html' => [ 'body' => [ 'input', // Void. 'div', /* Not void. */ ] ] ]); $actual = $xml->html(true); $expected = "<html>\n" . " <body>\n" . " <input/>\n" . " <div></div>\n" . " </body>\n" . "</html>"; \assert($actual === $expected, __($actual, $expected)); }); it('should return a node and the descendants as HTML string', function () { $xml = new FluidXml([ 'html' => [ 'body' => [ 'input', // Void. 'div', /* Not void. */ ] ] ]); $actual = $xml->query('//body/*')->html(); $expected = "<input/>\n" . "<div></div>"; \assert($actual === $expected, __($actual, $expected)); }); }); describe('.save()', function () { it('should be fluid', function () { $file = "{$this->out_dir}.test_save0.xml"; assert_is_fluid('save', $file); \unlink($file); }); it('should store the entire XML document in a file', function () { $xml = new FluidXml(); $xml->addChild('parent', true) ->addChild('child', 'content'); $file = "{$this->out_dir}.test_save1.xml"; $xml->save($file); $actual = \trim(\file_get_contents($file)); $expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" . "<doc>\n" . " <parent>\n" . " <child>content</child>\n" . " </parent>\n" . "</doc>"; \unlink($file); \assert($actual === $expected, __($actual, $expected)); }); it('should store a fragment of the XML document in a file', function () { $xml = new FluidXml(); $xml->addChild('parent', true) ->addChild('child', 'content'); $file = "{$this->out_dir}.test_save2.xml"; $xml->query('//child')->save($file); $actual = \trim(\file_get_contents($file)); $expected = "<child>content</child>"; \unlink($file); \assert($actual === $expected, __($actual, $expected)); }); it('should throw for not writable file', function () { $xml = new FluidXml(); $err_handler = \set_error_handler(function () {}); try { $xml->save('/.impossible/tmp/out.xml'); } catch (\Exception $e) { $actual = $e; } \set_error_handler($err_handler); assert_is_a($actual, \Exception::class); }); }); }); describe('FluidContext', function () { it('should be iterable returning the represented DOMNode objects', function () { $xml = new FluidXml(); $cx = $xml->addChild(['head', 'body'], true); $actual = $cx; assert_is_a($actual, \Iterator::class); $representation = []; foreach ($cx as $k => $v) { $actual = \is_int($k); $expected = true; \assert($actual === $expected, __($actual, $expected)); $actual = $v; assert_is_a($actual, \DOMNode::class); $representation[$k] = $v->nodeName; } $actual = $representation; $expected = [0 => 'head', 1 => 'body']; \assert($actual === $expected, __($actual, $expected)); }); describe('.__construct()', function () { it('should accept a DOMDocument', function () { $xml = new FluidXml(); $doc = new FluidDocument(); $handler = new FluidInsertionHandler($doc); $new_cx = new FluidContext($doc, $handler, $xml->dom()); $actual = $new_cx[0]; $expected = $xml->dom(); \assert($actual === $expected, __($actual, $expected)); }); it('should accept a DOMNode', function () { $xml = new FluidXml(); $cx = $xml->addChild(['head'], true); $doc = new FluidDocument(); $handler = new FluidInsertionHandler($doc); $new_cx = new FluidContext($doc, $handler, $cx[0]); $actual = $new_cx->array(); $expected = $cx->array(); \assert($actual === $expected, __($actual, $expected)); }); it('should accept an array of DOMNode', function () { $xml = new FluidXml(); $cx = $xml->addChild(['head', 'body'], true); $doc = new FluidDocument(); $handler = new FluidInsertionHandler($doc); $new_cx = new FluidContext($doc, $handler, $cx->array()); $actual = $new_cx->array(); $expected = $cx->array(); \assert($actual === $expected, __($actual, $expected)); }); it('should accept a DOMNodeList', function () { $xml = new FluidXml(); $cx = $xml->addChild(['head', 'body'], true); $dom = $xml->dom(); $domxp = new \DOMXPath($dom); $nodes = $domxp->query('/doc/*'); $doc = new FluidDocument(); $handler = new FluidInsertionHandler($doc); $new_cx = new FluidContext($doc, $handler, $nodes); $actual = $new_cx->array(); $expected = $cx->array(); \assert($actual === $expected, __($actual, $expected)); }); it('should accept a FluidContext', function () { $xml = new FluidXml(); $cx = $xml->addChild(['head', 'body'], true); $doc = new FluidDocument(); $handler = new FluidInsertionHandler($doc); $new_cx = new FluidContext($doc, $handler, $cx); $actual = $new_cx->array(); $expected = $cx->array(); \assert($actual === $expected, __($actual, $expected)); }); it('should throw for not supported document', function () { $doc = new FluidDocument(); $handler = new FluidInsertionHandler($doc); try { new FluidContext($doc, $handler, 'node'); } catch (\Exception $e) { $actual = $e; } assert_is_a($actual, \Exception::class); }); }); describe('[]', function () { it('should access the nodes inside the context', function () { $xml = new FluidXml(); $cx = $xml->addChild(['head', 'body'], true); $actual = $cx[0]; assert_is_a($actual, \DOMElement::class); $actual = $cx[1]; assert_is_a($actual, \DOMElement::class); }); it('should behave like an array', function () { $xml = new FluidXml(); $cx = $xml->addChild(['head', 'body', 'extra'], true); $actual = isset($cx[0]); $expected = true; \assert($actual === $expected, __($actual, $expected)); $actual = isset($cx[3]); $expected = false; \assert($actual === $expected, __($actual, $expected)); $actual = $cx[3]; $expected = null; \assert($actual === $expected, __($actual, $expected)); try { $cx[] = "value"; } catch (\Exception $e) { $actual = $e; } assert_is_a($actual, \Exception::class); unset($cx[1]); $actual = $cx[0]->nodeName; $expected = 'head'; \assert($actual === $expected, __($actual, $expected)); $actual = $cx[1]->nodeName; $expected = 'extra'; \assert($actual === $expected, __($actual, $expected)); }); }); describe('.array()', function () { it('should return an array of nodes inside the context', function () { $xml = new FluidXml(null); $a = $xml->array(); $actual = \is_array($a); $expected = True; \assert($actual === $expected, __($actual, $expected)); $actual = \count($a); $expected = 1; \assert($actual === $expected, __($actual, $expected)); $actual = $a; $expected = [ $xml->dom() ]; \assert($actual === $expected, __($actual, $expected)); $xml = new FluidXml(); $a = $xml->array(); $actual = \is_array($a); $expected = True; \assert($actual === $expected, __($actual, $expected)); $actual = \count($a); $expected = 1; \assert($actual === $expected, __($actual, $expected)); $actual = $a; $expected = [ $xml->dom()->documentElement ]; \assert($actual === $expected, __($actual, $expected)); $cx = $xml->addChild(['head', 'body'], true); $a = $cx->array(); $actual = \is_array($a); $expected = True; \assert($actual === $expected, __($actual, $expected)); $actual = \count($a); $expected = 2; \assert($actual === $expected, __($actual, $expected)); }); }); describe('.length()', function () { it('should return the number of nodes inside the context', function () { $xml = new FluidXml(); $cx = $xml->query('/*'); $actual = $xml->length(); $expected = 1; \assert($actual === $expected, __($actual, $expected)); $actual = $cx->length(); $expected = 1; \assert($actual === $expected, __($actual, $expected)); $cx = $xml->addChild(['child1', 'child2'], true); $actual = $cx->length(); $expected = 2; \assert($actual === $expected, __($actual, $expected)); $cx = $cx->addChild(['subchild1', 'subchild2', 'subchild3']); $actual = $cx->length(); $expected = 2; \assert($actual === $expected, __($actual, $expected)); $cx = $cx->addChild(['subchild4', 'subchild5', 'subchild6', 'subchild7'], true); $actual = $cx->length(); $expected = 8; \assert($actual === $expected, __($actual, $expected)); $expected = "<doc>\n" . " <child1>\n" . " <subchild1/>\n" . " <subchild2/>\n" . " <subchild3/>\n" . " <subchild4/>\n" . " <subchild5/>\n" . " <subchild6/>\n" . " <subchild7/>\n" . " </child1>\n" . " <child2>\n" . " <subchild1/>\n" . " <subchild2/>\n" . " <subchild3/>\n" . " <subchild4/>\n" . " <subchild5/>\n" . " <subchild6/>\n" . " <subchild7/>\n" . " </child2>\n" . "</doc>"; assert_equal_xml($xml, $expected); }); }); describe('.size()', function () { it('should behave like .length()', function () { $xml = new FluidXml(); $actual = $xml->size(); $expected = $xml->length(); \assert($actual === $expected, __($actual, $expected)); $cx = $xml->addChild('parent', true) ->addChild(['child1', 'child2']); $actual = $cx->size(); $expected = $cx->length(); \assert($actual === $expected, __($actual, $expected)); }); }); }); describe('FluidNamespace', function () { describe('.__construct()', function () { it('should accept an id, an uri and an optional mode flag', function () { $ns_id = 'x'; $ns_uri = 'x.com'; $ns_mode = FluidNamespace::MODE_EXPLICIT; $ns = new FluidNamespace($ns_id, $ns_uri); $actual = $ns->id(); $expected = $ns_id; \assert($actual === $expected, __($actual, $expected)); $actual = $ns->uri(); $expected = $ns_uri; \assert($actual === $expected, __($actual, $expected)); $actual = $ns->mode(); $expected = $ns_mode; \assert($actual === $expected, __($actual, $expected)); $ns_mode = FluidNamespace::MODE_IMPLICIT; $ns = new FluidNamespace($ns_id, $ns_uri, $ns_mode); $actual = $ns->mode(); $expected = $ns_mode; \assert($actual === $expected, __($actual, $expected)); }); }); describe('.id()', function () { it('should return the namespace id', function () { $ns_id = 'x'; $ns_uri = 'x.com'; $ns = new FluidNamespace($ns_id, $ns_uri); $actual = $ns->id(); $expected = $ns_id; \assert($actual === $expected, __($actual, $expected)); }); }); describe('.uri()', function () { it('should return the namespace uri', function () { $ns_id = 'x'; $ns_uri = 'x.com'; $ns = new FluidNamespace($ns_id, $ns_uri); $actual = $ns->uri(); $expected = $ns_uri; \assert($actual === $expected, __($actual, $expected)); }); }); describe('.mode()', function () { it('should return the namespace mode', function () { $ns_id = 'x'; $ns_uri = 'x.com'; $ns = new FluidNamespace($ns_id, $ns_uri); $ns_mode = FluidNamespace::MODE_EXPLICIT; $actual = $ns->mode(); $expected = $ns_mode; \assert($actual === $expected, __($actual, $expected)); $ns_mode = FluidNamespace::MODE_IMPLICIT; $ns = new FluidNamespace($ns_id, $ns_uri, $ns_mode); $actual = $ns->mode(); $expected = $ns_mode; \assert($actual === $expected, __($actual, $expected)); }); }); describe('.querify()', function () { it('should format an XPath query to use the namespace id', function () { $ns = new FluidNamespace('x', 'x.com'); $actual = $ns('current/child'); $expected = 'x:current/x:child'; \assert($actual === $expected, __($actual, $expected)); $actual = $ns('//current/child'); $expected = '//x:current/x:child'; \assert($actual === $expected, __($actual, $expected)); $ns = new FluidNamespace('x', 'x.com', FluidNamespace::MODE_IMPLICIT); $actual = $ns('current/child'); $expected = 'x:current/x:child'; \assert($actual === $expected, __($actual, $expected)); $actual = $ns('//current/child'); $expected = '//x:current/x:child'; \assert($actual === $expected, __($actual, $expected)); }); }); }); describe('FluidHelper', function () { describe(':isAnXmlString()', function () { it('should understand if a string is an XML document', function () { $xml = new FluidXml(); $actual = FluidHelper::isAnXmlString($xml->xml()); $expected = true; \assert($actual === $expected, __($actual, $expected)); $actual = FluidHelper::isAnXmlString(" \n \n \t" . $xml->xml()); $expected = true; \assert($actual === $expected, __($actual, $expected)); $actual = FluidHelper::isAnXmlString('item'); $expected = false; \assert($actual === $expected, __($actual, $expected)); }); }); describe(':domdocumentToHtml()', function () { it('should convert a DOMDocument instance to an HTML string without respecting void and not void tags.', function () { // This is only to analyze a condition (not used) for the code coverage reporter. FluidHelper::domdocumentToHtml((new FluidXml())->dom(), true); }); }); describe(':domdocumentToStringWithoutHeaders()', function () { it('should convert a DOMDocument instance to an XML string without the XML headers (declaration and stylesheets)', function () { $xml = new FluidXml(); $actual = FluidHelper::domdocumentToStringWithoutHeaders($xml->dom()); $expected = "<doc/>"; \assert($actual === $expected, __($actual, $expected)); $xml = new FluidXml('doc', ['stylesheet' => 'x.com/style.xsl']); $actual = FluidHelper::domdocumentToStringWithoutHeaders($xml->dom()); $expected = "<doc/>"; \assert($actual === $expected, __($actual, $expected)); }); }); describe(':domnodelistToString()', function () { it('should convert a DOMNodeList instance to an XML string', function () { $xml = new FluidXml(); $nodes = $xml->dom()->childNodes; $actual = FluidHelper::domnodelistToString($nodes); $expected = "<doc/>"; \assert($actual === $expected, __($actual, $expected)); }); }); describe(':domnodesToString()', function () { it('should convert an array of DOMNode instances to an XML string', function () { $xml = new FluidXml(); $nodes = [ $xml->dom()->documentElement ]; $actual = FluidHelper::domnodesToString($nodes); $expected = "<doc/>"; \assert($actual === $expected, __($actual, $expected)); }); }); describe('simplexmlToStringWithoutHeaders()', function () { it('should convert a SimpleXMLElement instance to an XML string without the XML headers (declaration and stylesheets)', function () { $xml = \simplexml_import_dom((new FluidXml())->dom()); $actual = FluidHelper::simplexmlToStringWithoutHeaders($xml); $expected = "<doc/>"; \assert($actual === $expected, __($actual, $expected)); }); }); }); describe('CssTranslator', function () { describe('.xpath()', function () { $hml = new FluidXml([ 'html' => [ 'body' => [ 'div' => [ [ 'p' => [ '@class' => 'a', '@id' => '123', [ 'span' ] ] ], [ 'h1' => [ '@class' => 'b' ] ], [ 'shape' => [ '@class' => 'c' ] ], [ 'p' => [ '@class' => 'a b' ] ], [ 'p' => [ '@class' => 'a' ] ], [ 'span' => [ '@class' => 'b' ] ], ]]]]); $hml->namespace('svg', 'http://svg.org'); $hml->query('//body') ->add('svg:svg', true) ->add('svg:shape') ->add('svg:shape'); it('should support the CSS selector A', function () use ($hml) { $actual = $hml->query('p')->array(); $expected = $hml->query('//p')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('p')->size(); $expected = 3; \assert($actual === $expected, __($actual, $expected)); }); it('should support the CSS selector ns|A', function () use ($hml) { $actual = $hml->query('svg|shape')->array(); $expected = $hml->query('//svg:shape')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('svg|shape')->size(); $expected = 2; \assert($actual === $expected, __($actual, $expected)); }); it('should support the CSS selector *|A', function () use ($hml) { $actual = $hml->query('*|shape')->array(); $expected = $hml->query('[local-name() = "shape"]')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('*|shape')->size(); $expected = 3; \assert($actual === $expected, __($actual, $expected)); }); it('should support the CSS selector :root', function () use ($hml) { $actual = $hml->query(':root')->array(); $expected = $hml->query('/*')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query(':root')->size(); $expected = 1; \assert($actual === $expected, __($actual, $expected)); }); it('should support the CSS selector #id', function () use ($hml) { $actual = $hml->query('#123')->array(); $expected = $hml->query('//*[@id="123"]')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('#123')->size(); $expected = 1; \assert($actual === $expected, __($actual, $expected)); }); it('should support the CSS selector .class.class', function () use ($hml) { $actual = $hml->query('.a')->array(); $expected = $hml->query('//p')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('.a')->size(); $expected = 3; \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('.a.b')->array(); $expected = $hml->query('//p[2]')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('.a.b')->size(); $expected = 1; \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('h1.b')->array(); $expected = $hml->query('//h1')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('h1.b')->size(); $expected = 1; \assert($actual === $expected, __($actual, $expected)); }); it('should support the CSS selector [attr]', function () use ($hml) { $actual = $hml->query('[class]')->array(); $expected = $hml->query('//div/*')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('[class]')->size(); $expected = 6; \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('[id]')->array(); $expected = $hml->query('//*[@id]')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('[id]')->size(); $expected = 1; \assert($actual === $expected, __($actual, $expected)); }); it('should support the CSS selector [attr="val"]', function () use ($hml) { $actual = $hml->query('p[id="123"]')->array(); $expected = $hml->query('//p[@id]')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('p[id="123"]')->size(); $expected = 1; \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('[class="a"]')->array(); $expected = $hml->query('//*[@class="a"]')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('[class="a"]')->size(); $expected = 2; \assert($actual === $expected, __($actual, $expected)); }); it('should support the CSS selector A B', function () use ($hml) { $actual = $hml->query('div span')->array(); $expected = $hml->query('//div//span')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('div span')->size(); $expected = 2; \assert($actual === $expected, __($actual, $expected)); }); it('should support the CSS selector A > B', function () use ($hml) { $actual = $hml->query('div > p')->array(); $expected = $hml->query('//div/p')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('div > p')->size(); $expected = 3; \assert($actual === $expected, __($actual, $expected)); }); it('should support the CSS selector A, B', function () use ($hml) { $actual = $hml->query('p, div')->array(); $expected = $hml->query('//p|//div')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('p, div')->size(); $expected = 4; \assert($actual === $expected, __($actual, $expected)); }); it('should support the CSS selector A + B', function () use ($hml) { $actual = $hml->query('p + p')->array(); $expected = $hml->query('//p[3]')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('p + p')->size(); $expected = 1; \assert($actual === $expected, __($actual, $expected)); }); it('should support the CSS selector A ~ B', function () use ($hml) { $actual = $hml->query('h1 ~ p')->array(); $expected = $hml->query('//p[2]|//p[3]')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query('h1 ~ p')->size(); $expected = 2; \assert($actual === $expected, __($actual, $expected)); }); it('should support mixing CSS selectors :root #123 span, div, :root .a', function () use ($hml) { $actual = $hml->query(':root #123 span, div, :root .a')->array(); $expected = $hml->query('//p/span|//div|//*[@class="a"]|//*[@class="a b"]')->array(); \assert($actual === $expected, __($actual, $expected)); $actual = $hml->query(':root #123 span, div, :root .a')->size(); $expected = 5; \assert($actual === $expected, __($actual, $expected)); }); }); });