From 7bdde22d68c07ef58f15e381776ffdae34b2eb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Fedor?= Date: Sun, 5 Jun 2022 01:53:10 +0200 Subject: [PATCH 1/5] Improve detection of element in query --- src/Translator.php | 17 +++++++++++------ test/phpunit/Helper/Helper.php | 23 +++++++++++++++++++++++ test/phpunit/TranslatorTest.php | 24 ++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/Translator.php b/src/Translator.php index 1409090..c1ad7ee 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -61,7 +61,7 @@ protected function convertSingleSelector(string $css):string { $thread = array_values($thread); $xpath = [$this->prefix]; - $prevType = ""; + $hasElement = false; foreach($thread as $threadKey => $currentThreadItem) { $next = isset($thread[$threadKey + 1]) ? $thread[$threadKey + 1] @@ -71,6 +71,7 @@ protected function convertSingleSelector(string $css):string { case "star": case "element": $xpath []= $currentThreadItem['content']; + $hasElement = true; break; case "pseudo": @@ -160,23 +161,26 @@ protected function convertSingleSelector(string $css):string { case "child": array_push($xpath, "/"); + $hasElement = false; break; case "id": array_push( $xpath, - ($prevType != "element" ? '*' : '') + ($hasElement ? '' : '*') . "[@id='{$currentThreadItem['content']}']" ); + $hasElement = true; break; case "class": // https://devhints.io/xpath#class-check array_push( $xpath, - (($prevType != "element" && $prevType != "class") ? '*' : '') + ($hasElement ? '' : '*') . "[contains(concat(' ',normalize-space(@class),' '),' {$currentThreadItem['content']} ')]" ); + $hasElement = true; break; case "sibling": @@ -184,11 +188,13 @@ protected function convertSingleSelector(string $css):string { $xpath, "/following-sibling::*[1]/self::" ); + $hasElement = false; break; case "attribute": - if(!$prevType) { + if(!$hasElement) { array_push($xpath, "*"); + $hasElement = true; } /** @var null|array> $detail */ @@ -257,10 +263,9 @@ protected function convertSingleSelector(string $css):string { case "descendant": array_push($xpath, "//"); + $hasElement = false; break; } - - $prevType = $currentThreadItem["type"]; } return implode("", $xpath); diff --git a/test/phpunit/Helper/Helper.php b/test/phpunit/Helper/Helper.php index 3ab9cf0..fb65de2 100644 --- a/test/phpunit/Helper/Helper.php +++ b/test/phpunit/Helper/Helper.php @@ -156,4 +156,27 @@ class Helper { HTML; + const HTML_SELECTORS = << + + + + + HTML Complex + + + + +
+
First content without ID
+
Content with ID
+
Second content without ID
+
Content with attribute 1
+
Content with attribute 2
+
Third content without ID
+
+ + + +HTML; } diff --git a/test/phpunit/TranslatorTest.php b/test/phpunit/TranslatorTest.php index 82943e6..adc6d6c 100644 --- a/test/phpunit/TranslatorTest.php +++ b/test/phpunit/TranslatorTest.php @@ -397,4 +397,28 @@ public function testSquareBracketsNameAttribute() { self::assertEquals(3, $choiceInputs->length); } + + public function testCombinedSelectors() { + $document = new DOMDocument("1.0", "UTF-8"); + $document->loadHTML(Helper::HTML_SELECTORS); + $xpath = new DOMXPath($document); + + $classIdTranslator = new Translator(".content#content-element"); + $classAttr2Translator = new Translator(".content[data-attr='2']"); + + $titleEl = $xpath->query($classIdTranslator)->item(0); + self::assertEquals("Content with ID", $titleEl->nodeValue); + + $attr2El = $xpath->query($classAttr2Translator)->item(0); + self::assertEquals("Content with attribute 2", $attr2El->nodeValue); + } + + public function testChildWithAttribute() { + $document = new DOMDocument("1.0", "UTF-8"); + $document->loadHTML(Helper::HTML_CHECKBOX); + $xpath = new DOMXPath($document); + $choiceTranslator = new Translator("form [name]"); + $choiceInputs = $xpath->query($choiceTranslator); + self::assertEquals(3, $choiceInputs->length); + } } From 3ff654c7655894cad40d77d6d38e1d7a60c945c3 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 8 Jun 2022 12:10:50 +0100 Subject: [PATCH 2/5] style: reformat --- test/phpunit/TranslatorTest.php | 48 ++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/test/phpunit/TranslatorTest.php b/test/phpunit/TranslatorTest.php index adc6d6c..46631bf 100644 --- a/test/phpunit/TranslatorTest.php +++ b/test/phpunit/TranslatorTest.php @@ -228,7 +228,7 @@ public function testClassSelector() { )->item(0); self::assertSame($navElement, $navElement2); - $navElement3 = $xpath->query( + $navElement3 = $xpath->query( new Translator("nav.c-menu.main-selection") )->item(0); self::assertSame($navElement, $navElement3); @@ -398,27 +398,27 @@ public function testSquareBracketsNameAttribute() { self::assertEquals(3, $choiceInputs->length); } - public function testCombinedSelectors() { - $document = new DOMDocument("1.0", "UTF-8"); - $document->loadHTML(Helper::HTML_SELECTORS); - $xpath = new DOMXPath($document); - - $classIdTranslator = new Translator(".content#content-element"); - $classAttr2Translator = new Translator(".content[data-attr='2']"); - - $titleEl = $xpath->query($classIdTranslator)->item(0); - self::assertEquals("Content with ID", $titleEl->nodeValue); - - $attr2El = $xpath->query($classAttr2Translator)->item(0); - self::assertEquals("Content with attribute 2", $attr2El->nodeValue); - } - - public function testChildWithAttribute() { - $document = new DOMDocument("1.0", "UTF-8"); - $document->loadHTML(Helper::HTML_CHECKBOX); - $xpath = new DOMXPath($document); - $choiceTranslator = new Translator("form [name]"); - $choiceInputs = $xpath->query($choiceTranslator); - self::assertEquals(3, $choiceInputs->length); - } + public function testCombinedSelectors() { + $document = new DOMDocument("1.0", "UTF-8"); + $document->loadHTML(Helper::HTML_SELECTORS); + $xpath = new DOMXPath($document); + + $classIdTranslator = new Translator(".content#content-element"); + $classAttr2Translator = new Translator(".content[data-attr='2']"); + + $titleEl = $xpath->query($classIdTranslator)->item(0); + self::assertEquals("Content with ID", $titleEl->nodeValue); + + $attr2El = $xpath->query($classAttr2Translator)->item(0); + self::assertEquals("Content with attribute 2", $attr2El->nodeValue); + } + + public function testChildWithAttribute() { + $document = new DOMDocument("1.0", "UTF-8"); + $document->loadHTML(Helper::HTML_CHECKBOX); + $xpath = new DOMXPath($document); + $choiceTranslator = new Translator("form [name]"); + $choiceInputs = $xpath->query($choiceTranslator); + self::assertEquals(3, $choiceInputs->length); + } } From 73d6fad28e9a39d011aa86131eed5883454bc26d Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 8 Jun 2022 12:12:03 +0100 Subject: [PATCH 3/5] style: reformat --- test/phpunit/Helper/Helper.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/phpunit/Helper/Helper.php b/test/phpunit/Helper/Helper.php index fb65de2..1f3bfa9 100644 --- a/test/phpunit/Helper/Helper.php +++ b/test/phpunit/Helper/Helper.php @@ -169,11 +169,11 @@ class Helper {
First content without ID
-
Content with ID
-
Second content without ID
-
Content with attribute 1
-
Content with attribute 2
-
Third content without ID
+
Content with ID
+
Second content without ID
+
Content with attribute 1
+
Content with attribute 2
+
Third content without ID
From 5445e83437c948ba7e00f6b711670a48c4cfc2e8 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 8 Jun 2022 12:14:53 +0100 Subject: [PATCH 4/5] test: multiple named selects --- test/phpunit/TranslatorTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/phpunit/TranslatorTest.php b/test/phpunit/TranslatorTest.php index 46631bf..22fada2 100644 --- a/test/phpunit/TranslatorTest.php +++ b/test/phpunit/TranslatorTest.php @@ -421,4 +421,15 @@ public function testChildWithAttribute() { $choiceInputs = $xpath->query($choiceTranslator); self::assertEquals(3, $choiceInputs->length); } + + public function testMultipleNamedElements() { + $document = new DOMDocument("1.0", "UTF-8"); + $document->loadHTML(Helper::HTML_SELECTS); + $xpath = new DOMXPath($document); + $translator = new Translator("form [name='from'], form [name='to']"); + $selectElements = $xpath->query($translator); + self::assertEquals(2, $selectElements->length); + self::assertSame("from", $selectElements->item(0)->getAttribute("name")); + self::assertSame("to", $selectElements->item(1)->getAttribute("name")); + } } From ea925a1ac22d5d49fc7a85134e01bd2e30d3924f Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Fri, 12 Jan 2024 17:09:59 +0000 Subject: [PATCH 5/5] test: add more tests for last-of-type and nth-of-type --- test/phpunit/TranslatorTest.php | 36 ++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/test/phpunit/TranslatorTest.php b/test/phpunit/TranslatorTest.php index 43ec5d8..eaac612 100644 --- a/test/phpunit/TranslatorTest.php +++ b/test/phpunit/TranslatorTest.php @@ -115,6 +115,40 @@ public function testFirstOfTypeNthOfTypeLastOfType() { self::assertEquals('table', $matches->item(1)->nodeValue); } + public function testLastOfType() { + $document = new DOMDocument("1.0", "UTF-8"); + $document->loadHTML(Helper::HTML_CHECKBOX); + + $xpath = new DOMXPath($document); + $selector = new Translator("form label:last-of-type input"); + + $matches = $xpath->query($selector); + self::assertEquals(1, $this->count($matches)); + /** @var \DOMElement $matchingInputElement */ + $matchingInputElement = $matches->item(0); + self::assertEquals( + "3", + $matchingInputElement->getAttribute("value") + ); + } + + public function testNthOfType() { + $document = new DOMDocument("1.0", "UTF-8"); + $document->loadHTML(Helper::HTML_CHECKBOX); + + $xpath = new DOMXPath($document); + $selector = new Translator("form label:nth-of-type(2) input"); + + $matches = $xpath->query($selector); + self::assertEquals(1, $this->count($matches)); + /** @var \DOMElement $matchingInputElement */ + $matchingInputElement = $matches->item(0); + self::assertEquals( + "2", + $matchingInputElement->getAttribute("value") + ); + } + public function testFirstNthLastChild() { $document = new DOMDocument("1.0", "UTF-8"); $document->loadHTML('

Track & field champions:

@@ -296,7 +330,7 @@ public function testCaseSensitivityHtmlMode() { 0, $xpath->query($attributeValueCaseSensitive)->length ); - + $tagNameCaseInsensitive = new Translator( "dIv" );