* @license The GNU Lesser GPL (LGPL) or an MIT-like license. */ namespace QueryPath\Tests; /** @addtogroup querypath_tests Tests * Unit tests and regression tests for DOMQuery. */ use QueryPath\DOMQuery; use \Masterminds\HTML5; /** */ //require_once 'PHPUnit/Autoload.php'; require __DIR__ . '/../../../vendor/autoload.php'; require_once __DIR__ . '/TestCase.php'; define('DATA_FILE', __DIR__ . '/../../data.xml'); define('DATA_HTML_FILE', __DIR__ . '/../../data.html'); define('NO_WRITE_FILE', __DIR__ . '/../../no-write.xml'); define('MEDIUM_FILE', __DIR__ . '/../../amplify.xml'); define('HTML_IN_XML_FILE', __DIR__ . '/../../html.xml'); /** * Tests for DOM Query. Primarily, this is focused on the DomQueryImpl * class which is exposed through the DomQuery interface and the dq() * factory function. * @ingroup querypath_tests */ class DOMQueryTest extends TestCase { /** * @group basic */ public function testDOMQueryConstructors() { // From XML file $file = DATA_FILE; $qp = qp($file); $this->assertEquals(1, count($qp->get())); $this->assertTrue($qp->get(0) instanceof \DOMNode); // From XML file with context $cxt = stream_context_create(); $qp = qp($file, NULL, array('context' => $cxt)); $this->assertEquals(1, count($qp->get())); $this->assertTrue($qp->get(0) instanceof \DOMNode); // From XML string $str = ''; $qp = qp($str); $this->assertEquals(1, count($qp->get())); $this->assertTrue($qp->get(0) instanceof \DOMNode); // From SimpleXML $str = ''; $qp = qp(simplexml_load_string($str)); $this->assertEquals(1, count($qp->get())); $this->assertTrue($qp->get(0) instanceof \DOMNode); // Test from DOMDocument $qp = qp(\DOMDocument::loadXML($str)); $this->assertEquals(1, count($qp->get())); $this->assertTrue($qp->get(0) instanceof \DOMNode); // Now with a selector: $qp = qp($file, '#head'); $this->assertEquals(1, count($qp->get())); $this->assertEquals($qp->get(0)->tagName, 'head'); // Test HTML: $htmlFile = DATA_HTML_FILE; $qp = qp($htmlFile); $this->assertEquals(1, count($qp->get())); $this->assertTrue($qp->get(0) instanceof \DOMNode); // Test with another DOMQuery. $qp = qp($qp); $this->assertEquals(1, count($qp->get())); $this->assertTrue($qp->get(0) instanceof \DOMNode); // Test from array of DOMNodes $array = $qp->get(); $qp = qp($array); $this->assertEquals(1, count($qp->get())); $this->assertTrue($qp->get(0) instanceof \DOMNode); } /** * Test alternate constructors. * @group basic */ public function testDOMQueryHtmlConstructors() { $qp = htmlqp(\QueryPath::HTML_STUB); $this->assertEquals(1, count($qp->get())); $this->assertTrue($qp->get(0) instanceof \DOMNode); // Bad BR tag. $borken = '
'; $qp = htmlqp($borken); $this->assertEquals(1, count($qp->get())); $this->assertTrue($qp->get(0) instanceof \DOMNode); // XHTML Faker $borken = '
'; $qp = htmlqp($borken); $this->assertEquals(1, count($qp->get())); $this->assertTrue($qp->get(0) instanceof \DOMNode); // HTML in a file that looks like XML. $qp = htmlqp(HTML_IN_XML_FILE); $this->assertEquals(1, count($qp->get())); $this->assertTrue($qp->get(0) instanceof \DOMNode); // HTML5 $html5 = new \Masterminds\HTML5(); $dom = $html5->loadHTML(\QueryPath::HTML_STUB); qp($dom,'html'); // Stripping #13 (CR) from HTML. $borken = '

' . chr(13) . '

'; $this->assertFalse(strpos(htmlqp($borken)->html(), ' '), 'Test that CRs are not encoded.'); // Regression for #58: Make sure we aren't getting encoded. $borken = ''; $this->assertFalse(strpos(htmlqp($borken)->html(), ' '), 'Test that LF is not encoded.'); // Low ASCII in a file $borken = '

' . chr(27) . '

'; $this->assertEquals(1, htmlqp($borken, '#after')->size()); } public function testForTests() { $qp_methods = get_class_methods('\QueryPath\DOMQuery'); $test_methods = get_class_methods('\QueryPath\Tests\DOMQueryTest'); $ignore = array("__construct", "__call", "__clone", "get", "getOptions", "setMatches", "toArray", "getIterator"); $test_methods = array_map('strtolower', $test_methods); foreach($qp_methods as $q) { if(in_array($q, $ignore)) continue; $this->assertTrue(in_array(strtolower("test".$q), $test_methods), $q . ' does not have a test method.'); } } public function testOptionXMLEncoding() { $xml = qp(NULL, NULL, array('encoding' => 'iso-8859-1'))->append('')->xml(); $iso_found = preg_match('/iso-8859-1/', $xml) == 1; $this->assertTrue($iso_found, 'Encoding should be iso-8859-1 in ' . $xml . 'Found ' . $iso_found); $iso_found = preg_match('/utf-8/', $xml) == 1; $this->assertFalse($iso_found, 'Encoding should not be utf-8 in ' . $xml); $xml = qp('', NULL, array('encoding' => 'iso-8859-1'))->xml(); $iso_found = preg_match('/utf-8/', $xml) == 1; $this->assertTrue($iso_found, 'Encoding should be utf-8 in ' . $xml); $iso_found = preg_match('/iso-8859-1/', $xml) == 1; $this->assertFalse($iso_found, 'Encoding should not be utf-8 in ' . $xml); } public function testQPAbstractFactory() { $options = array('QueryPath_class' => '\QueryPath\Tests\QueryPathExtended'); $qp = qp(NULL, NULL, $options); $this->assertTrue($qp instanceof QueryPathExtended, 'Is instance of extending class.'); $this->assertTrue($qp->foonator(), 'Has special foonator() function.'); } public function testQPAbstractFactoryIterating() { $xml = ''; $options = array('QueryPath_class' => '\QueryPath\Tests\QueryPathExtended'); foreach(qp($xml, 'i', $options) as $item) { $this->assertTrue($item instanceof QueryPathExtended, 'Is instance of extending class.'); } } /** * @expectedException \QueryPath\Exception */ public function testFailedCall() { // This should hit __call() and then fail. qp()->fooMethod(); } /** * @expectedException \QueryPath\Exception */ public function testFailedObjectConstruction() { qp(new \stdClass()); } /** * @expectedException \QueryPath\ParseException */ public function testFailedHTTPLoad() { try { qp('http://localhost:8877/no_such_file.xml'); } catch (Exception $e) { //print $e->getMessage(); throw $e; } } /** * @expectedException \QueryPath\ParseException */ public function testFailedHTTPLoadWithContext() { try { qp('http://localhost:8877/no_such_file.xml', NULL, array('foo' => 'bar')); } catch (Exception $e) { //print $e->getMessage(); throw $e; } } /** * @expectedException \QueryPath\ParseException */ public function testFailedParseHTMLElement() { try { qp('&foonator;', NULL); } catch (Exception $e) { //print $e->getMessage(); throw $e; } } /** * @expectedException \QueryPath\ParseException */ public function testFailedParseXMLElement() { try { qp('foonator;', NULL); } catch (Exception $e) { //print $e->getMessage(); throw $e; } } public function testIgnoreParserWarnings() { $qp = @qp('BAD!', NULL, array('ignore_parser_warnings' => TRUE)); $this->assertTrue(strpos($qp->html(), 'BAD!') !== FALSE); \QueryPath\Options::merge(array('ignore_parser_warnings' => TRUE)); $qp = @qp('BAD!'); $this->assertTrue(strpos($qp->html(), 'BAD!') !== FALSE); $qp = @qp('BAD!'); $this->assertTrue(strpos($qp->html(), 'BAD!') !== FALSE, $qp->html()); \QueryPath\Options::set(array()); // Reset to empty options. } /** * @expectedException \QueryPath\ParseException */ public function testFailedParseNonMarkup() { try { qp('<23dfadf', NULL); } catch (Exception $e) { //print $e->getMessage(); throw $e; } } /** * @expectedException \QueryPath\ParseException */ public function testFailedParseEntity() { try { qp('&foonator;', NULL); } catch (Exception $e) { //print $e->getMessage(); throw $e; } } public function testReplaceEntitiesOption() { $path = ''; $xml = qp($path, NULL, array('replace_entities' => TRUE))->xml('&')->xml(); $this->assertTrue(strpos($xml, '&') !== FALSE); $xml = qp($path, NULL, array('replace_entities' => TRUE))->html('&')->xml(); $this->assertTrue(strpos($xml, '&') !== FALSE); $xml = qp($path, NULL, array('replace_entities' => TRUE))->xhtml('&')->xml(); $this->assertTrue(strpos($xml, '&') !== FALSE); \QueryPath\Options::set(array('replace_entities' => TRUE)); $this->assertTrue(strpos($xml, '&') !== FALSE); \QueryPath\Options::set(array()); } /** * @group basic */ public function testFind() { $file = DATA_FILE; $qp = qp($file)->find('#head'); $this->assertEquals(1, count($qp->get())); $this->assertEquals($qp->get(0)->tagName, 'head'); $this->assertEquals('inner', qp($file)->find('.innerClass')->tag()); $string = 'Test'; $qp = qp($string)->find('root'); $this->assertEquals(1, count($qp->get()), 'Check tag.'); $this->assertEquals($qp->get(0)->tagName, 'root'); $string = 'Test'; $qp = qp($string)->find('.findme'); $this->assertEquals(1, count($qp->get()), 'Check class.'); $this->assertEquals($qp->get(0)->tagName, 'root'); } public function testFindInPlace() { $file = DATA_FILE; $qp = qp($file)->find('#head'); $this->assertEquals(1, count($qp->get())); $this->assertEquals($qp->get(0)->tagName, 'head'); $this->assertEquals('inner', qp($file)->find('.innerClass')->tag()); $string = 'Test'; $qp = qp($string)->find('root'); $this->assertEquals(1, count($qp->get()), 'Check tag.'); $this->assertEquals($qp->get(0)->tagName, 'root'); $string = 'Test'; $qp = qp($string)->find('.findme'); $this->assertEquals(1, count($qp->get()), 'Check class.'); $this->assertEquals($qp->get(0)->tagName, 'root'); } /** * @group basic */ public function testTop() { $file = DATA_FILE; $qp = qp($file)->find('li'); $this->assertGreaterThan(2, $qp->size()); $this->assertEquals(1, $qp->top()->size()); // Added for QP 2.0 $xml = ''; $qp = qp($xml, 'l'); $this->assertEquals(3, $qp->size()); $this->assertEquals(2, $qp->top('u')->size()); } /** * @group basic */ public function testAttr() { $file = DATA_FILE; $qp = qp($file)->find('#head'); $this->assertEquals(1, $qp->size()); $this->assertEquals($qp->get(0)->getAttribute('id'), $qp->attr('id')); $qp->attr('foo', 'bar'); $this->assertEquals('bar', $qp->attr('foo')); $qp->attr(array('foo2' => 'bar', 'foo3' => 'baz')); $this->assertEquals('baz', $qp->attr('foo3')); // Check magic nodeType attribute: $this->assertEquals(XML_ELEMENT_NODE, qp($file)->find('#head')->attr('nodeType')); // Since QP 2.1 $xml = ''; $qp = qp($xml, 'one'); $attrs = $qp->attr(); $this->assertEquals(3, count($attrs), 'Three attributes'); $this->assertEquals('1', $attrs['a1'], 'Attribute a1 has value 1.'); } /** * @group basic */ public function testHasAttr() { $xml = '
'; $this->assertFalse(qp($xml, 'root')->hasAttr('foo')); $this->assertTrue(qp($xml, 'div')->hasAttr('foo')); $xml = '
'; $this->assertTrue(qp($xml, 'div')->hasAttr('foo')); $xml = '
'; $this->assertFalse(qp($xml, 'div')->hasAttr('foo')); $xml = '
'; $this->assertFalse(qp($xml, 'div')->hasAttr('foo')); } public function testVal() { $qp = qp('', 'bar'); $this->assertEquals('test', $qp->val()); $qp = qp('', 'bar')->val('test'); $this->assertEquals('test', $qp->attr('value')); } public function testCss() { $file = DATA_FILE; $this->assertEquals('foo: bar;', qp($file, 'unary')->css('foo', 'bar')->attr('style')); $this->assertEquals('foo: bar;', qp($file, 'unary')->css('foo', 'bar')->css()); $this->assertEquals('foo: bar;', qp($file, 'unary')->css(array('foo' =>'bar'))->css()); // Issue #28: Setting styles in sequence should not result in the second // style overwriting the first style: $qp = qp($file, 'unary')->css('color', 'blue')->css('background-color', 'white'); $expects = 'color: blue;background-color: white;'; $actual = $qp->css(); $this->assertEquals(bin2hex($expects), bin2hex($actual), 'Two css calls should result in two attrs.'); // Make sure array merges work. $qp = qp($file, 'unary')->css('a','a')->css(array('b'=>'b', 'c'=>'c')); $this->assertEquals('a: a;b: b;c: c;', $qp->css()); // Make sure that second assignment overrides first assignment. $qp = qp($file, 'unary')->css('a','a')->css(array('b'=>'b', 'a'=>'c')); $this->assertEquals('a: c;b: b;', $qp->css()); } public function testRemoveAttr() { $file = DATA_FILE; $qp = qp($file, 'inner')->removeAttr('class'); $this->assertEquals(2, $qp->size()); $this->assertFalse($qp->get(0)->hasAttribute('class')); } public function testEq() { $file = DATA_FILE; $qp = qp($file)->find('li')->eq(0); $this->assertEquals(1, $qp->size()); $this->assertEquals($qp->attr('id'), 'one'); $this->assertEquals(1, qp($file, 'inner')->eq(0)->size()); $this->assertEquals(1, qp($file, 'li')->eq(0)->size()); $this->assertEquals("Hello", qp($file, 'li')->eq(0)->text()); $this->assertEquals("Last", qp($file, 'li')->eq(3)->text()); } public function testIs() { $file = DATA_FILE; $this->assertTrue(qp($file)->find('#one')->is('#one')); $this->assertTrue(qp($file)->find('li')->is('#one')); $qp = qp($file)->find('#one'); $ele = $qp->get(0); $this->assertTrue($qp->top('#one')->is($ele)); $qp = qp($file)->find('#one'); $ele = $qp->get(0); $ele2 = $qp->top('#two')->get(0); $list = new \SplDoublyLinkedList(); $list->push($ele); $list->push($ele2); $this->assertEquals(2, count($list)); //$this->assertEquals(2, ) $this->assertTrue($qp->top('#one,#two')->is($list)); } public function testIndex() { $xml = ''; $qp = qp($xml, 'bar'); $e1 = $qp->get(0); $this->assertEquals(0, $qp->find('bar')->index($e1)); $this->assertFalse($qp->top()->find('#two')->index($e1)); } public function testFilter() { $file = DATA_FILE; $this->assertEquals(1, qp($file)->filter('li')->size()); $this->assertEquals(2, qp($file, 'inner')->filter('li')->size()); $this->assertEquals('inner-two', qp($file, 'inner')->filter('li')->eq(1)->attr('id')); } public function testFilterPreg() { $xml = '
Foo
Moo
'; $qp = qp($xml, 'div')->filterPreg('/Foo/'); $this->assertEquals(1, $qp->Size()); // Check to make sure textContent is collected correctly. $xml = '
Hello World
'; $qp = qp($xml, 'div')->filterPreg('/Hello\sWorld/'); $this->assertEquals(1, $qp->Size()); } public function testFilterLambda() { $file = DATA_FILE; // Get all evens: $l = 'return (($index + 1) % 2 == 0);'; $this->assertEquals(2, qp($file, 'li')->filterLambda($l)->size()); } public function filterCallbackFunction($index, $item) { return (($index + 1) % 2 == 0); } public function testFilterCallback() { $file = DATA_FILE; $cb = array($this, 'filterCallbackFunction'); $this->assertEquals(2, qp($file, 'li')->filterCallback($cb)->size()); } /** * @expectedException \QueryPath\Exception */ public function testFailedFilterCallback() { $file = DATA_FILE; $cb = array($this, 'noSuchFunction'); qp($file, 'li')->filterCallback($cb)->size(); } /** * @expectedException \QueryPath\Exception */ public function testFailedMapCallback() { $file = DATA_FILE; $cb = array($this, 'noSuchFunction'); qp($file, 'li')->map($cb)->size(); } public function testNot() { $file = DATA_FILE; // Test with selector $qp = qp($file, 'li:odd')->not('#one'); $this->assertEquals(2, $qp->size()); // Test with DOM Element $qp = qp($file, 'li'); $el = $qp->branch()->filter('#one')->get(0); $this->assertTrue($el instanceof \DOMElement, "Is DOM element."); $this->assertEquals(4, $qp->not($el)->size()); // Test with array of DOM Elements $qp = qp($file, 'li'); $arr = $qp->get(); $this->assertEquals(count($arr), $qp->size()); array_shift($arr); $this->assertEquals(1, $qp->not($arr)->size()); } public function testSlice() { $file = DATA_FILE; // There are five
  • elements $qp = qp($file, 'li')->slice(1); $this->assertEquals(4, $qp->size()); // The first item in the matches should be #two. $this->assertEquals('two', $qp->attr('id')); // THe last item should be #five $this->assertEquals('five', $qp->eq(3)->attr('id')); // This should not throw an error. $this->assertEquals(4, qp($file, 'li')->slice(1, 9)->size()); $this->assertEquals(0, qp($file, 'li')->slice(9)->size()); // The first item should be #two, the last #three $qp = qp($file, 'li')->slice(1, 2); $this->assertEquals(2, $qp->size()); $this->assertEquals('two', $qp->attr('id')); $this->assertEquals('three', $qp->eq(1)->attr('id')); } public function mapCallbackFunction($index, $item) { if ($index == 1) { return FALSE; } if ($index == 2) { return array(1, 2, 3); } return $index; } public function testMap() { $file = DATA_FILE; $fn = 'mapCallbackFunction'; $this->assertEquals(7, qp($file, 'li')->map(array($this, $fn))->size()); } public function eachCallbackFunction($index, $item) { if ($index < 2) { qp($item)->attr('class', 'test'); } else { return FALSE; } } public function testEach() { $file = DATA_FILE; $fn = 'eachCallbackFunction'; $res = qp($file, 'li')->each(array($this, $fn)); $this->assertEquals(5, $res->size()); $this->assertFalse($res->get(4)->getAttribute('class') === NULL); $this->assertEquals('test', $res->eq(1)->attr('class')); // Test when each runs out of things to test before returning. $res = qp($file, '#one')->each(array($this, $fn)); $this->assertEquals(1, $res->size()); } /** * @expectedException \QueryPath\Exception */ public function testEachOnInvalidCallback() { $file = DATA_FILE; $fn = 'eachCallbackFunctionFake'; $res = qp($file, 'li')->each(array($this, $fn)); } public function testEachLambda() { $file = DATA_FILE; $fn = 'qp($item)->attr("class", "foo");'; $res = qp($file, 'li')->eachLambda($fn); $this->assertEquals('foo', $res->eq(1)->attr('class')); } public function testDeepest() { $str = ' '; $deepest = qp($str)->deepest(); $this->assertEquals(2, $deepest->size()); $this->assertEquals('four', $deepest->get(0)->tagName); $this->assertEquals('banana', $deepest->get(1)->tagName); $deepest = qp($str, 'one')->deepest(); $this->assertEquals(2, $deepest->size()); $this->assertEquals('four', $deepest->get(0)->tagName); $this->assertEquals('banana', $deepest->get(1)->tagName); $str = ' CDATA '; $this->assertEquals(1, qp($str)->deepest()->size()); } public function testTag() { $file = DATA_FILE; $this->assertEquals('li', qp($file, 'li')->tag()); } public function testAppend() { $file = DATA_FILE; $this->assertEquals(1, qp($file,'unary')->append('')->find(':root > unary > test')->size()); $qp = qp($file,'#inner-one')->append('
  • '); $appended = $qp->find('#appended'); $this->assertEquals(1, $appended->size()); $this->assertNull($appended->get(0)->nextSibling); $this->assertEquals(2, qp($file, 'inner')->append('')->top()->find('test')->size()); $this->assertEquals(2, qp($file, 'inner')->append(qp(''))->top()->find('test')->size()); $this->assertEquals(4, qp($file, 'inner')->append(qp('', 'test'))->top()->find('test')->size()); // Issue #6: This seems to break on Debian Etch systems... no idea why. $this->assertEquals('test', qp()->append('')->top()->tag()); // Issue #7: Failure issues warnings // This seems to be working as expected -- libxml emits // parse errors. //$this->assertEquals(NULL, qp()->append('')->append($simp); $this->assertEquals(1, $qp->find('root')->size()); // Test with replace entities turned on: $qp = qp($file, 'root', array('replace_entities' => TRUE))->append('

    »

    '); // Note that we are using a UTF-8 » character, not an ASCII 187. This seems to cause // problems on some Windows IDEs. So here we do it the ugly way. $utf8raquo = '

    ' . mb_convert_encoding(chr(187), 'utf-8', 'iso-8859-1') . '

    '; //$this->assertEquals('

    »

    ', $qp->find('p')->html(), 'Entities are decoded to UTF-8 correctly.'); $this->assertEquals($utf8raquo, $qp->find('p')->html(), 'Entities are decoded to UTF-8 correctly.'); // Test with empty, mainly to make sure it doesn't explode. $this->assertTrue(qp($file)->append('') instanceof DOMQuery); } /** * @expectedException \QueryPath\ParseException */ public function testAppendBadMarkup() { $file = DATA_FILE; try{ qp($file, 'root')->append(''); } catch (Exception $e) { //print $e->getMessage(); throw $e; } } /** * @expectedException \QueryPath\Exception */ public function testAppendBadObject() { $file = DATA_FILE; try{ qp($file, 'root')->append(new \stdClass); } catch (Exception $e) { //print $e->getMessage(); throw $e; } } public function testAppendTo() { $file = DATA_FILE; $dest = qp('', 'dest'); $qp = qp($file,'li')->appendTo($dest); $this->assertEquals(5, $dest->find(':root li')->size()); } public function testPrepend() { $file = DATA_FILE; $this->assertEquals(1, qp($file,'unary')->prepend('')->find(':root > unary > test')->size()); $qp = qp($file,'#inner-one')->prepend('
  • ')->find('#appended'); $this->assertEquals(1, $qp->size()); $this->assertNull($qp->get(0)->previousSibling); // Test repeated insert $this->assertEquals(2, qp($file,'inner')->prepend('')->top()->find('test')->size()); $this->assertEquals(4, qp($file,'inner')->prepend(qp('', 'test'))->top()->find('test')->size()); } public function testPrependTo() { $file = DATA_FILE; $dest = qp('', 'dest'); $qp = qp($file,'li')->prependTo($dest); $this->assertEquals(5, $dest->find(':root li')->size()); } public function testBefore() { $file = DATA_FILE; $this->assertEquals(1, qp($file,'unary')->before('')->find(':root > test ~ unary')->size()); $this->assertEquals(1, qp($file,'unary')->before('')->top('head ~ test')->size()); $this->assertEquals('unary', qp($file,'unary')->before('')->top(':root > test')->get(0)->nextSibling->tagName); // Test repeated insert $this->assertEquals(2, qp($file,'inner')->before('')->top()->find('test')->size()); $this->assertEquals(4, qp($file,'inner')->before(qp('', 'test'))->top()->find('test')->size()); } public function testAfter() { $file = DATA_FILE; $this->assertEquals(1, qp($file,'unary')->after('')->top(':root > unary ~ test')->size()); $this->assertEquals('unary', qp($file,'unary')->after('')->top(':root > test')->get(0)->previousSibling->tagName); $this->assertEquals(2, qp($file,'inner')->after('')->top()->find('test')->size()); $this->assertEquals(4, qp($file,'inner')->after(qp('', 'test'))->top()->find('test')->size()); } public function testInsertBefore() { $file = DATA_FILE; $dest = qp('', 'dest'); $qp = qp($file,'li')->insertBefore($dest); $this->assertEquals(5, $dest->top(':root > li')->size()); $this->assertEquals('li', $dest->end()->find('dest')->get(0)->previousSibling->tagName); } public function testInsertAfter() { $file = DATA_FILE; $dest = qp('', 'dest'); $qp = qp($file,'li')->insertAfter($dest); //print $dest->get(0)->ownerDocument->saveXML(); $this->assertEquals(5, $dest->top(':root > li')->size()); } public function testReplaceWith() { $file = DATA_FILE; $qp = qp($file,'unary')->replaceWith('')->top('test'); //print $qp->get(0)->ownerDocument->saveXML(); $this->assertEquals(1, $qp->size()); $qp = qp($file,'unary')->replaceWith(qp('', 'test')); //print $qp->get(0)->ownerDocument->saveXML(); $this->assertEquals(2, $qp->top()->find('test')->size()); } public function testReplaceAll() { $qp1 = qp(''); $doc = qp('')->get(0)->ownerDocument; $qp2 = $qp1->find('l')->replaceAll('m', $doc); $this->assertEquals(2, $qp2->top()->find('l')->size()); } public function testUnwrap() { // Unwrap center, and make sure junk goes away. $xml = '
    '; $qp = qp($xml, 'center')->unwrap(); $this->assertEquals('root', $qp->top('center')->parent()->tag()); $this->assertEquals(0, $qp->top('junk')->size()); // Make sure it works on two nodes in the same parent. $xml = '
    '; $qp = qp($xml, 'center')->unwrap(); // Make sure they were unwrapped $this->assertEquals('root', $qp->top('center')->parent()->tag()); // Make sure both get copied. $this->assertEquals(2, $qp->top('center')->size()); // Make sure they are in order. $this->assertEquals('2', $qp->top('center:last')->attr('id')); // Test on root element. $xml = '
    '; $qp = qp($xml, 'center')->unwrap(); $this->assertEquals('center', $qp->top()->tag()); } /** * @expectedException \QueryPath\Exception */ public function testFailedUnwrap() { // Cannot unwrap the root element. $xml = ''; $qp = qp($xml, 'root')->unwrap(); $this->assertEquals('center', $qp->top()->tag()); } public function testWrap() { $file = DATA_FILE; $xml = qp($file,'unary')->wrap(''); $this->assertTrue($xml instanceof DOMQuery); $xml = qp($file,'unary')->wrap('')->get(0)->ownerDocument->saveXML(); $this->assertEquals(1, qp($xml, '#testWrap')->get(0)->childNodes->length); $xml = qp($file,'unary')->wrap(qp('', 'test'))->get(0)->ownerDocument->saveXML(); $this->assertEquals(1, qp($xml, '#testWrap')->get(0)->childNodes->length); $xml = qp($file,'li')->wrap('')->get(0)->ownerDocument->saveXML(); $this->assertEquals(5, qp($xml, '.testWrap')->size()); $xml = qp($file,'li')->wrap('
    ')->get(0)->ownerDocument->saveXML(); $this->assertEquals(5, qp($xml, '.testWrap > inside > center > li')->size()); } public function testWrapAll() { $file = DATA_FILE; $xml = qp($file,'unary')->wrapAll(''); $this->assertTrue($xml instanceof DOMQuery); $xml = qp($file,'unary')->wrapAll('')->get(0)->ownerDocument->saveXML(); $this->assertEquals(1, qp($xml, '#testWrap')->get(0)->childNodes->length); $xml = qp($file,'unary')->wrapAll(qp('', 'test'))->get(0)->ownerDocument->saveXML(); $this->assertEquals(1, qp($xml, '#testWrap')->get(0)->childNodes->length); $xml = qp($file,'li')->wrapAll('
    ')->get(0)->ownerDocument->saveXML(); $this->assertEquals(5, qp($xml, '.testWrap > inside > center > li')->size()); } public function testWrapInner() { $file = DATA_FILE; $this->assertTrue(qp($file,'#inner-one')->wrapInner('') instanceof DOMQuery); $xml = qp($file,'#inner-one')->wrapInner('')->get(0)->ownerDocument->saveXML(); // FIXME: 9 includes text nodes. Should fix this. $this->assertEquals(9, qp($xml, '.testWrap')->get(0)->childNodes->length); $xml = qp($file,'inner')->wrapInner('')->get(0)->ownerDocument->saveXML(); $this->assertEquals(9, qp($xml, '.testWrap')->get(0)->childNodes->length); $this->assertEquals(3, qp($xml, '.testWrap')->get(1)->childNodes->length); $qp = qp($file,'inner')->wrapInner(qp('', 'test')); $this->assertEquals(2, $qp->find('inner > .testWrap')->size()); $this->assertEquals(0, $qp->find('.ignore')->size()); } public function testRemove() { $file = DATA_FILE; $qp = qp($file, 'li'); $start = $qp->size(); $finish = $qp->remove()->size(); $this->assertEquals($start, $finish); $this->assertEquals(0, $qp->find(':root li')->size()); // Test for Issue #55 $data = 'test FAIL'; $qp = qp($data); $rem = $qp->remove('b'); $this->assertEquals(' FAIL', $rem->text()); $this->assertEquals('test', $qp->text()); // Test for Issue #63 $qp = qp($data); $rem = $qp->remove('noSuchElement'); $this->assertEquals(0, count($rem)); } public function testHasClass() { $file = DATA_FILE; $this->assertTrue(qp($file, '#inner-one')->hasClass('innerClass')); $file = DATA_FILE; $this->assertFalse(qp($file, '#inner-one')->hasClass('noSuchClass')); } public function testAddClass() { $file = DATA_FILE; $this->assertTrue(qp($file, '#inner-one')->addClass('testClass')->hasClass('testClass')); } public function testRemoveClass() { $file = DATA_FILE; // The add class tests to make sure that this works with multiple values. $this->assertFalse(qp($file, '#inner-one')->removeClass('innerClass')->hasClass('innerClass')); $this->assertTrue(qp($file, '#inner-one')->addClass('testClass')->removeClass('innerClass')->hasClass('testClass')); } public function testAdd() { $file = DATA_FILE; $this->assertEquals(7, qp($file, 'li')->add('inner')->size()); } public function testEnd() { $file = DATA_FILE; $this->assertEquals(2, qp($file, 'inner')->find('li')->end()->size()); } public function testAndSelf() { $file = DATA_FILE; $this->assertEquals(7, qp($file, 'inner')->find('li')->andSelf()->size()); } public function testChildren() { $file = DATA_FILE; $this->assertEquals(5, qp($file, 'inner')->children()->size()); foreach (qp($file, 'inner')->children('li') as $kid) { $this->assertEquals('li', $kid->tag()); } $this->assertEquals(5, qp($file, 'inner')->children('li')->size()); $this->assertEquals(1, qp($file, ':root')->children('unary')->size()); } public function testRemoveChildren() { $file = DATA_FILE; $this->assertEquals(0, qp($file, '#inner-one')->removeChildren()->find('li')->size()); } public function testContents() { $file = DATA_FILE; $this->assertGreaterThan(5, qp($file, 'inner')->contents()->size()); // Two cdata nodes and one element node. $this->assertEquals(3, qp($file, '#inner-two')->contents()->size()); // Issue #51: Under certain recursive conditions, this returns error. // Warning: Whitespace is important in the markup beneath. $xml = '
    Hello
    how are you
    fine thank you
    and you ?
    '; $cr = $this->contentsRecurse(qp($xml)); $this->assertEquals(14, count($cr), implode("\n", $cr)); } public function testNS() { $xml = 'test'; $q = qp($xml, "e"); $this->assertEquals(1, $q->size()); $this->assertEquals("foo:bar", $q->ns()); } /** * Helper function for testContents(). * Based on problem reported in issue 51. */ private function contentsRecurse($source, &$pack = array()) { //static $i = 0; //static $filter = "%d. Node type: %d, Content: '%s'\n"; $children = $source->contents(); //$node = $source->get(0); $pack[] = 1; //sprintf($filter, ++$i, $node->nodeType, $source->html()); foreach ($children as $child) { $pack += $this->contentsRecurse($child, $pack); } return $pack; } public function testSiblings() { $file = DATA_FILE; $this->assertEquals(3, qp($file, '#one')->siblings()->size()); $this->assertEquals(2, qp($file, 'unary')->siblings('inner')->size()); } public function testXinclude() { } public function testHTML() { $file = DATA_FILE; $qp = qp($file, 'unary'); $html = 'test'; $this->assertEquals($html, $qp->html($html)->find('b')->html()); $html = 'foobar'; // We expect a DocType to be prepended: $this->assertEquals('html(), 0, 9)); // Check that HTML is not added to empty finds. Note the # is for a special // case. $this->assertEquals('', qp($html, '#nonexistant')->html('

    Hello

    ')->html()); $this->assertEquals('', qp($html, 'nonexistant')->html('

    Hello

    ')->html()); // We expect NULL if the document is empty. $this->assertNull(qp()->html()); // Non-DOMNodes should not be rendered: $fn = 'mapCallbackFunction'; $this->assertNull(qp($file, 'li')->map(array($this, $fn))->html()); } public function testInnerHTML() { $html = '
    Test

    Again

    '; $this->assertEquals('Test

    Again

    ', qp($html,'#me')->innerHTML()); } public function testInnerXML() { $html = '
    Test

    Again1

    '; $test = 'Test

    Again1

    '; $this->assertEquals($test, qp($html,'#me')->innerXML()); $html = '
    Test

    Again2

    '; $test = 'Test

    Again2

    '; $this->assertEquals($test, qp($html,'#me')->innerXML()); $html = '
    '; $test = ''; $this->assertEquals($test, qp($html,'#me')->innerXML()); $html = 'test'; $test = 'test'; $this->assertEquals($test, qp($html,'#me')->innerXML()); } public function testInnerXHTML() { $html = '
    Test

    Again

    '; $this->assertEquals('Test

    Again

    ', qp($html,'#me')->innerXHTML()); // Regression for issue #10: Tags should not be unary (e.g. we want , not , not '; $xhtml = qp($xml)->xhtml(); //throw new Exception($xhtml); // Look for a properly formatted BR unary tag: $regex = '/
    /'; $this->assertRegExp($regex, $xhtml, 'BR should have a closing tag.'); // Look for a properly formatted HR tag: $regex = '/
    /'; $this->assertRegExp($regex, $xhtml, 'BR should have a closing tag.'); // Ensure that script tag is not collapsed: $regex = '/ foobar'; if (!ob_start()) die ("Could not start OB."); qp($xml, 'tml')->writeXML(); $out = ob_get_contents(); ob_end_clean(); // We expect an XML declaration at the top. $this->assertEquals('writeXML($name); $this->assertTrue(file_exists($name)); $this->assertTrue(qp($name) instanceof DOMQuery); unlink($name); } public function testWriteXHTML() { $xml = 'foobar'; if (!ob_start()) die ("Could not start OB."); qp($xml, 'tml')->writeXHTML(); $out = ob_get_contents(); ob_end_clean(); // We expect an XML declaration at the top. $this->assertEquals(' foobar'; if (!ob_start()) die ("Could not start OB."); qp($xml, 'html')->writeXHTML(); $out = ob_get_contents(); ob_end_clean(); // We expect an XML declaration at the top. $this->assertEquals('writeXHTML($name); $this->assertTrue(file_exists($name)); $this->assertTrue(qp($name) instanceof DOMQuery); unlink($name); // Regression for issue #10 (keep closing tags in XHTML) $xhtml = 'foo
    bar'; if (!ob_start()) die ("Could not start OB."); qp($xhtml, 'html')->writeXHTML(); $out = ob_get_contents(); ob_end_clean(); $pattern = '/<\/script>/'; $this->assertRegExp($pattern, $out, 'Should be closing script tag.'); $pattern = '/<\/br>/'; $this->assertRegExp($pattern, $out, 'Should be closing br tag.'); } /** * @expectedException \QueryPath\IOException */ public function testFailWriteXML() { try { qp()->writeXML('./test/no-writing.xml'); } catch (Exception $e) { //print $e->getMessage(); throw $e; } } /** * @expectedException \QueryPath\IOException */ public function testFailWriteXHTML() { try { qp()->writeXHTML('./test/no-writing.xml'); } catch (\QueryPath\IOException $e) { //print $e->getMessage(); throw $e; } } /** * @expectedException \QueryPath\IOException */ public function testFailWriteHTML() { try { qp('')->writeXML('./test/no-writing.xml'); } catch (\QueryPath\IOException $e) { // print $e->getMessage(); throw $e; } } public function testWriteHTML() { $xml = 'foobar'; if (!ob_start()) die ("Could not start OB."); qp($xml, 'tml')->writeHTML(); $out = ob_get_contents(); ob_end_clean(); // We expect a doctype declaration at the top. $this->assertEquals('foo bar'; if (!ob_start()) die ("Could not start OB."); qp($xml, 'tml')->writeHTML(); $out = ob_get_contents(); ob_end_clean(); // We expect a doctype declaration at the top. $this->assertEquals('foo bar'; if (!ob_start()) die ("Could not start OB."); qp($xml, 'tml')->writeHTML(); $out = ob_get_contents(); ob_end_clean(); // We expect a doctype declaration at the top. $this->assertEquals('writeXML($name); $this->assertTrue(file_exists($name)); $this->assertTrue(qp($name) instanceof DOMQuery); unlink($name); } public function testText() { $xml = '
    Text A
    Text B
    '; $this->assertEquals('Text AText B', qp($xml)->text()); $this->assertEquals('Foo', qp($xml, 'div')->eq(0)->text('Foo')->text()); $this->assertEquals('BarBar', qp($xml, 'div')->text('Bar')->text()); } public function testTextAfter() { $xml = '
    After
    After2
    After3'; $this->assertEquals('AfterAfter2', qp($xml, 'br')->textAfter()); $this->assertEquals('Blarg', qp($xml, 'foo')->textAfter('Blarg')->top('foo')->textAfter()); } public function testTextBefore() { $xml = 'Before
    Before2
    Before3
    '; $this->assertEquals('BeforeBefore2', qp($xml, 'br')->textBefore()); $this->assertEquals('Blarg', qp($xml, 'foo')->textBefore('Blarg')->top('foo')->textBefore()); } public function testTextImplode() { $xml = '
    Text A
    Text B
    '; $this->assertEquals('Text A, Text B', qp($xml, 'div')->textImplode()); $this->assertEquals('Text A--Text B', qp($xml, 'div')->textImplode('--')); $xml = '
    Text A
    Text B
    '; $this->assertEquals('Text A , Text B', qp($xml, 'div')->textImplode()); $xml = '
    Text A
    Text B
    '; $this->assertEquals('Text A , Text B', qp($xml, 'div')->textImplode(', ', TRUE)); // Test with empties $xml = '
    Text A
    Text B
    '; $this->assertEquals('Text A- -Text B', qp($xml, 'div')->textImplode('-', FALSE)); } public function testChildrenText() { $xml = ' NOT ME!
    Text A
    Text B
    '; $this->assertEquals('Text A , Text B', qp($xml, 'div')->childrenText(', ', TRUE), 'Just inner text.'); } public function testNext() { $file = DATA_FILE; $this->assertEquals('inner', qp($file, 'unary')->next()->tag()); $this->assertEquals('foot', qp($file, 'inner')->next()->eq(1)->tag()); $this->assertEquals('foot', qp($file, 'unary')->next('foot')->tag()); // Regression test for issue eabrand identified: $qp = qp(\QueryPath::HTML_STUB, 'body')->append('

    Hello

    Goodbye

    ') ->children('p') ->after('

    new paragraph

    '); $testarray = array('new paragraph', 'Goodbye', 'new paragraph'); //throw new Exception($qp->top()->xml()); $qp = $qp->top('p:first-of-type'); $this->assertEquals('Hello', $qp->text(), "Test First P " . $qp->top()->html()); $i = 0; while($qp->next('p')->html() != null) { $qp = $qp->next('p'); $this->assertEquals(1, count($qp)); $this->assertEquals($testarray[$i], $qp->text(), $i . " didn't match " . $qp->top()->xml() ); $i++; } $this->assertEquals(3, $i); // $this->assertEquals('new paragraph', $qp->next()->text(), "Test Newly Added P"); // $this->assertEquals('Goodbye', $qp->next()->text(), "Test third P"); // $this->assertEquals('new paragraph', $qp->next()->text(), "Test Other Newly Added P"); } public function testPrev() { $file = DATA_FILE; $this->assertEquals('head', qp($file, 'unary')->prev()->tag()); $this->assertEquals('inner', qp($file, 'inner')->prev()->eq(1)->tag()); $this->assertEquals('head', qp($file, 'foot')->prev('head')->tag()); } public function testNextAll() { $file = DATA_FILE; $this->assertEquals(3, qp($file, '#one')->nextAll()->size()); $this->assertEquals(2, qp($file, 'unary')->nextAll('inner')->size()); } public function testPrevAll() { $file = DATA_FILE; $this->assertEquals(3, qp($file, '#four')->prevAll()->size()); $this->assertEquals(2, qp($file, 'foot')->prevAll('inner')->size()); } public function testParent() { $file = DATA_FILE; $this->assertEquals('root', qp($file, 'unary')->parent()->tag()); $this->assertEquals('root', qp($file, 'li')->parent('root')->tag()); $this->assertEquals(2, qp($file, 'li')->parent()->size()); } public function testClosest() { $file = DATA_FILE; $this->assertEquals('root', qp($file, 'li')->parent('root')->tag()); $xml = '
    '; $this->assertEquals(2, qp($xml, 'b')->closest('.foo')->size()); } public function testParents() { $file = DATA_FILE; // Three: two inners and a root. $this->assertEquals(3, qp($file, 'li')->parents()->size()); $this->assertEquals('root', qp($file, 'li')->parents('root')->tag()); } public function testCloneAll() { $file = DATA_FILE; // Shallow test $qp = qp($file, 'unary'); $one = $qp->get(0); $two = $qp->cloneAll()->get(0); $this->assertTrue($one !== $two); $this->assertEquals('unary', $two->tagName); // Deep test: make sure children are also cloned. $qp = qp($file, 'inner'); $one = $qp->find('li')->get(0); $two = $qp->top('inner')->cloneAll(TRUE)->findInPlace('li')->get(0); $this->assertEquals('li', $two->tagName); $this->assertTrue($one !== $two); } public function testBranch() { $qp = qp(\QueryPath::HTML_STUB); $branch = $qp->branch(); $branch->top('title')->text('Title'); $qp->top('title')->text('FOOOOO')->top(); $qp->find('body')->text('This is the body'); $this->assertEquals($qp->top('title')->text(), $branch->top('title')->text(), $branch->top()->html()); $qp = qp(\QueryPath::HTML_STUB); $branch = $qp->branch('title'); $branch->find('title')->text('Title'); $qp->find('body')->text('This is the body'); $this->assertEquals($qp->top()->find('title')->text(), $branch->text()); } public function testXpath() { $file = DATA_FILE; $this->assertEquals('head', qp($file)->xpath("//*[@id='head']")->tag()); } public function test__clone() { $file = DATA_FILE; $qp = qp($file, 'inner:first-of-type'); $qp2 = clone $qp; $this->assertFalse($qp === $qp2); $qp2->findInPlace('li')->attr('foo', 'bar'); $this->assertEquals('', $qp->find('li')->attr('foo')); $this->assertEquals('bar', $qp2->attr('foo'), $qp2->top()->xml()); } public function testStub() { $this->assertEquals(1, qp(\QueryPath::HTML_STUB)->find('title')->size()); } public function testIterator() { $qp = qp(\QueryPath::HTML_STUB, 'body')->append('
  • '); $this->assertEquals(4, $qp->find('li')->size()); $i = 0; foreach ($qp->find('li') as $li) { ++$i; $li->text('foo'); } $this->assertEquals(4, $i); $this->assertEquals('foofoofoofoo', $qp->top()->find('li')->text()); } public function testModeratelySizedDocument() { $this->assertEquals(1, qp(MEDIUM_FILE)->size()); $contents = file_get_contents(MEDIUM_FILE); $this->assertEquals(1, qp($contents)->size()); } /** * @deprecated */ public function testSize() { $file = DATA_FILE; $qp = qp($file, 'li'); $this->assertEquals(5, $qp->size()); } public function testCount() { $file = DATA_FILE; $qp = qp($file, 'li'); $this->assertEquals(5, $qp->count()); // Test that this is exposed to PHP's Countable logic. $this->assertEquals(5, count(qp($file, 'li'))); } public function testLength() { // Test that the length attribute works exactly the same as size. $file = DATA_FILE; $qp = qp($file, 'li'); $this->assertEquals(5, $qp->length); } public function testDocument() { $file = DATA_FILE; $doc1 = new \DOMDocument('1.0'); $doc1->load($file); $qp = qp($doc1); $this->assertEquals($doc1, $qp->document()); // Ensure that adding to the DOMDocument is accessible to QP: $ele = $doc1->createElement('testDocument'); $doc1->documentElement->appendChild($ele); $this->assertEquals(1, $qp->find('testDocument')->size()); } /* public function test__get() { // Test that other properties are not interferred with by __get(). $file = DATA_FILE; $options = array('QueryPath_class' => 'QueryPathExtended'); $foo = qp($file,'li', $options)->foo; $this->assertEquals('bar', $foo); } */ /** * @ expectedException \QueryPath\Exception */ /* public function testFailed__get() { // This should generate an error because 'last' is protected. qp(DATA_FILE)->last; } */ public function testDetach() { $file = DATA_FILE; $qp = qp($file, 'li'); $start = $qp->size(); $finish = $qp->detach()->size(); $this->assertEquals($start, $finish); $this->assertEquals(0, $qp->find(':root li')->size()); } public function testAttach() { $file = DATA_FILE; $qp = qp($file, 'li'); $start = $qp->size(); $finish = $qp->detach()->size(); $dest = qp('', 'dest'); $qp = $qp->attach($dest); $this->assertEquals(5, $dest->find(':root li')->size()); } public function testEmptyElement() { $file = DATA_FILE; $this->assertEquals(0, qp($file, '#inner-two')->emptyElement()->find('li')->size()); $this->assertEquals('', qp($file, '#inner-two')->emptyElement()->html()); // Make sure text children get wiped out, too. $this->assertEquals('', qp($file, 'foot')->emptyElement()->text()); } public function testHas() { $file = DATA_FILE; // Test with DOMNode object $qp = qp($file, 'foot'); $selector = $qp->get(0); $qp = $qp->top('root')->has($selector); // This should have one element named 'root'. $this->assertEquals(1, $qp->size(), 'One element is a parent of foot'); $this->assertEquals('root', $qp->tag(), 'Root has foot.'); // Test with CSS selector $qp = qp($file, 'root')->has('foot'); // This should have one element named 'root'. $this->assertEquals(1, $qp->size(), 'One element is a parent of foot'); $this->assertEquals('root', $qp->tag(), 'Root has foot.'); // Test multiple matches. $qp = qp($file, '#docRoot, #inner-two')->has('#five'); $this->assertEquals(2, $qp->size(), 'Two elements are parents of #five'); $this->assertEquals('inner', $qp->get(0)->tagName, 'Inner has li.'); /* $this->assertEquals(qp($file, '#one')->children()->get(), qp($file, '#inner-one')->has($selector)->get(), "Both should be empty/false"); $qp = qp($file, 'root')->children("inner"); $selector = qp($file, '#two'); $this->assertNotEquals(qp($file, '#head'), qp($file, '#inner-one')->has($selector)); $this->assertEquals(qp($file, 'root'), qp($file, 'root')->has($selector), "Should both have 1 element - root"); */ } public function testNextUntil() { $file = DATA_FILE; $this->assertEquals(3, qp($file, '#one')->nextUntil()->size()); $this->assertEquals(2, qp($file, 'li')->nextUntil('#three')->size()); } public function testPrevUntil() { $file = DATA_FILE; $this->assertEquals(3, qp($file, '#four')->prevUntil()->size()); $this->assertEquals(2, qp($file, 'foot')->prevUntil('unary')->size()); } public function testEven() { $file = DATA_FILE; $this->assertEquals(1, qp($file, 'inner')->even()->size()); $this->assertEquals(2, qp($file, 'li')->even()->size()); } public function testOdd() { $file = DATA_FILE; $this->assertEquals(1, qp($file, 'inner')->odd()->size()); $this->assertEquals(3, qp($file, 'li')->odd()->size()); } public function testFirst() { $file = DATA_FILE; $this->assertEquals(1, qp($file, 'inner')->first()->size()); $this->assertEquals(1, qp($file, 'li')->first()->size()); $this->assertEquals("Hello", qp($file, 'li')->first()->text()); } public function testFirstChild() { $file = DATA_FILE; $this->assertEquals(1, qp($file, '#inner-one')->firstChild()->size()); $this->assertEquals("Hello", qp($file, '#inner-one')->firstChild()->text()); } public function testLast() { $file = DATA_FILE; $this->assertEquals(1, qp($file, 'inner')->last()->size()); $this->assertEquals(1, qp($file, 'li')->last()->size()); $this->assertEquals('', qp($file, 'li')->last()->text()); } public function testLastChild() { $file = DATA_FILE; $this->assertEquals(1, qp($file, '#inner-one')->lastChild()->size()); $this->assertEquals("Last", qp($file, '#inner-one')->lastChild()->text()); } public function testParentsUntil() { $file = DATA_FILE; // Three: two inners and a root. $this->assertEquals(3, qp($file, 'li')->parentsUntil()->size()); $this->assertEquals(2, qp($file, 'li')->parentsUntil('root')->size()); } public function testSort() { $xml = '1521'; // Canary. $qp = qp($xml, 'i'); $expect = array(1, 5, 2, 1); foreach($qp as $item) { $this->assertEquals(array_shift($expect), $item->text()); } // Test simple ordering. $comp = function (\DOMNode $a, \DOMNode $b) { if ($a->textContent == $b->textContent) { return 0; } return $a->textContent > $b->textContent ? 1 : -1; }; $qp = qp($xml, 'i')->sort($comp); $expect = array(1, 1, 2, 5); foreach($qp as $item) { $this->assertEquals(array_shift($expect), $item->text()); } $comp = function (\DOMNode $a, \DOMNode $b) { $qpa = qp($a); $qpb = qp($b); if ($qpa->text() == $qpb->text()) { return 0; } return $qpa->text()> $qpb->text()? 1 : -1; }; $qp = qp($xml, 'i')->sort($comp); $expect = array(1, 1, 2, 5); foreach($qp as $item) { $this->assertEquals(array_shift($expect), $item->text()); } // Test DOM re-ordering $comp = function (\DOMNode $a, \DOMNode $b) { if ($a->textContent == $b->textContent) { return 0; } return $a->textContent > $b->textContent ? 1 : -1; }; $qp = qp($xml, 'i')->sort($comp, TRUE); $expect = array(1, 1, 2, 5); foreach($qp as $item) { $this->assertEquals(array_shift($expect), $item->text()); } $res = $qp->top()->xml(); $expect_xml = '1125'; $this->assertXmlStringEqualsXmlString($expect_xml, $res); } /** * Regression test for issue #14. */ public function testRegressionFindOptimizations() { $xml = ' Test '; // From inside, should not be able to find outside. $this->assertEquals(0, qp($xml, '#inside')->find('#outside')->size()); $xml = ' Test '; // From inside, should not be able to find outside. $this->assertEquals(0, qp($xml, '.inside')->find('.outside')->size()); } public function testDataURL() { $text = 'Hi!'; // Base-64 encoded value would be SGkh $xml = ''; $qp = qp($xml, 'item')->dataURL('secret', $text, 'text/plain'); $this->assertEquals(1, $qp->top('item[secret]')->size(), 'One attr should be added.'); $this->assertEquals('data:text/plain;base64,SGkh', $qp->attr('secret'), 'Attr value should be data URL.'); $result = $qp->dataURL('secret'); $this->assertEquals(2, count($result), 'Should return two-array.'); $this->assertEquals($text, $result['data'] , 'Should return original data, decoded.'); $this->assertEquals('text/plain', $result['mime'], 'Should return the original MIME'); } public function testEncodeDataURL() { $data = \QueryPath::encodeDataURL('Hi!', 'text/plain'); $this->assertEquals('data:text/plain;base64,SGkh', $data); } } /** * A simple mock for testing qp()'s abstract factory. * * @ingroup querypath_tests */ class QueryPathExtended extends DOMQuery { public $foo = 'bar'; public function foonator() { return TRUE; } }