From c2a9bf08f13bbd761c12bacfc4d6e8f1f8634e0b Mon Sep 17 00:00:00 2001 From: Jakub Zalas Date: Mon, 30 Sep 2019 21:51:42 +0100 Subject: [PATCH] Fix the :only-of-type pseudo class selector --- .../Tests/XPath/TranslatorTest.php | 33 +++++++++++++++++-- .../XPath/Extension/PseudoClassExtension.php | 6 ++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.php b/src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.php index 625096315d..a718fbd6f2 100644 --- a/src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.php +++ b/src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.php @@ -98,7 +98,7 @@ class TranslatorTest extends TestCase $elements = $document->xpath($translator->cssToXPath($css)); $this->assertCount(\count($elementsId), $elements); foreach ($elements as $element) { - $this->assertTrue(\in_array($element->attributes()->id, $elementsId)); + $this->assertContains((string) $element->attributes()->id, $elementsId); } } @@ -116,7 +116,7 @@ class TranslatorTest extends TestCase $this->assertCount(\count($elementsId), $elementsId); foreach ($elements as $element) { if (null !== $element->attributes()->id) { - $this->assertTrue(\in_array($element->attributes()->id, $elementsId)); + $this->assertContains((string) $element->attributes()->id, $elementsId); } } libxml_clear_errors(); @@ -137,6 +137,33 @@ class TranslatorTest extends TestCase $this->assertCount($count, $elements); } + public function testOnlyOfTypeFindsSingleChildrenOfGivenType() + { + $translator = new Translator(); + $translator->registerExtension(new HtmlExtension($translator)); + $document = new \DOMDocument(); + $document->loadHTML(<<<'HTML' + + +

+ A +

+

+ B + C +

+ + +HTML +); + + $xpath = new \DOMXPath($document); + $nodeList = $xpath->query($translator->cssToXPath('span:only-of-type')); + + $this->assertSame(1, $nodeList->length); + $this->assertSame('A', $nodeList->item(0)->textContent); + } + public function getXpathLiteralTestData() { return [ @@ -175,7 +202,7 @@ class TranslatorTest extends TestCase ['e:first-of-type', '*/e[position() = 1]'], ['e:last-of-type', '*/e[position() = last()]'], ['e:only-child', "*/*[(name() = 'e') and (last() = 1)]"], - ['e:only-of-type', 'e[last() = 1]'], + ['e:only-of-type', 'e[count(preceding-sibling::e)=0 and count(following-sibling::e)=0]'], ['e:empty', 'e[not(*) and not(string-length())]'], ['e:EmPTY', 'e[not(*) and not(string-length())]'], ['e:root', 'e[not(parent::*)]'], diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php index 27fe47f9a5..288a9e695e 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php @@ -123,11 +123,13 @@ class PseudoClassExtension extends AbstractExtension */ public function translateOnlyOfType(XPathExpr $xpath) { - if ('*' === $xpath->getElement()) { + $element = $xpath->getElement(); + + if ('*' === $element) { throw new ExpressionErrorException('"*:only-of-type" is not implemented.'); } - return $xpath->addCondition('last() = 1'); + return $xpath->addCondition(sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element)); } /**