diff --git a/composer.json b/composer.json index f54bc89..416ee1b 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,15 @@ } }, + "scripts": { + "phpunit": "vendor/bin/phpunit --configuration test/phpunit/phpunit.xml", + "phpstan": "vendor/bin/phpstan analyse --level 6 src", + "test": [ + "@phpunit", + "@phpstan" + ] + }, + "authors": [ { "name": "Greg Bowler", diff --git a/src/Translator.php b/src/Translator.php index 7bb1126..e5802fd 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -12,6 +12,7 @@ class Translator { . '|(#(?P[\w-]*))' . '|(\.(?P[\w-]*))' . '|(?P\s*\+\s*)' + . '|(?P\s*~\s*)' . "|(\[(?P[\w-]*)((?P[=~$|^*]+)(?P(.+\[\]'?)|[^\]]+))*\])+" . '|(?P\s+)' . '/'; @@ -24,8 +25,8 @@ class Translator { const EQUALS_STARTS_WITH = "^="; public function __construct( - protected string $cssSelector, - protected string $prefix = ".//", + protected string $cssSelector, + protected string $prefix = ".//", protected bool $htmlMode = true ) { } @@ -198,7 +199,7 @@ protected function convertSingleSelector(string $css):string { "[last()]" ); } - break; + break; } break; @@ -235,6 +236,14 @@ protected function convertSingleSelector(string $css):string { $hasElement = false; break; + case "subsequentsibling": + array_push( + $xpath, + "/following-sibling::" + ); + $hasElement = false; + break; + case "attribute": if(!$hasElement) { array_push($xpath, "*"); diff --git a/test/phpunit/TranslatorTest.php b/test/phpunit/TranslatorTest.php index 43ec5d8..cbfe3f9 100644 --- a/test/phpunit/TranslatorTest.php +++ b/test/phpunit/TranslatorTest.php @@ -210,6 +210,31 @@ public function testSibling() { ); } + public function testSubsequentSibling() { + $document = new DOMDocument("1.0", "UTF-8"); + $document->loadHTML(Helper::HTML_COMPLEX); + $xpath = new DOMXPath($document); + + $translator = new Translator("main header ~ div"); + $elements = $xpath->query($translator); + + self::assertEquals(2, $elements->length); + self::assertEquals(".//main//header/following-sibling::div", (string)$translator); + + $detailsOnly = new Translator("main header ~ div.details"); + self::assertEquals(1, $xpath->query($detailsOnly)->length); + } + + public function testSubsequentSibling_spaces() { + $translator1 = new Translator("main header ~ div"); + $translator2 = new Translator("main header~div"); + $translator3 = new Translator("main header ~div"); + + self::assertSame((string)$translator1, (string)$translator2); + self::assertSame((string)$translator1, (string)$translator3); + self::assertSame((string)$translator2, (string)$translator3); + } + public function testDescendant() { $document = new DOMDocument("1.0", "UTF-8"); $document->loadHTML(Helper::HTML_COMPLEX); @@ -296,7 +321,7 @@ public function testCaseSensitivityHtmlMode() { 0, $xpath->query($attributeValueCaseSensitive)->length ); - + $tagNameCaseInsensitive = new Translator( "dIv" );