<?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));
});
});
});
|