From 1f10bfaf653ac2aed22e7909b66c90352af737e3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zo Date: Wed, 6 Nov 2024 17:43:24 +0000 Subject: [PATCH 1/5] Fix for :nth-child(n+B) to select B-th and all following elements --- .gitignore | 4 +++- CHANGELOG.md | 4 ++++ src/CSS/QueryPathEventHandler.php | 10 ++++++++-- tests/QueryPath/CSS/QueryPathEventHandlerTest.php | 7 +++++++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index e1d95c1a..32df0b84 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,6 @@ docs/* doc/* test/fakepear vendor/ -composer.lock \ No newline at end of file +composer.lock +.phpunit.result.cache +.idea/ diff --git a/CHANGELOG.md b/CHANGELOG.md index f7f201ec..d248dc55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ QueryPath Changelog =========================== +# 4.0.2 + +- Fix for :nth-child(n+B) to select B-th and all following elements + # 4.0.1 - Only define global functions qp(), htmlqp(), and html5qp() if they haven't been defined already. diff --git a/src/CSS/QueryPathEventHandler.php b/src/CSS/QueryPathEventHandler.php index 43e06e3a..ab5b8b70 100644 --- a/src/CSS/QueryPathEventHandler.php +++ b/src/CSS/QueryPathEventHandler.php @@ -734,9 +734,15 @@ protected function parseAnB($rule) throw new ParseException("nth-child value is invalid."); } - // Each of these is legal: 1, -1, and -. '-' is shorthand for -1. + // Each of these is legal: 1, -1, - and . '-' is shorthand for -1. is shorthand for 1 $aVal = trim($rule[0]); - $aVal = ($aVal == '-') ? -1 : (int) $aVal; + if ($aVal === '') { + $aVal = 1; + } elseif ($aVal === '-') { + $aVal = -1; + } else { + $aVal = (int) $aVal; + } $bVal = ! empty($rule[1]) ? (int) trim($rule[1]) : 0; diff --git a/tests/QueryPath/CSS/QueryPathEventHandlerTest.php b/tests/QueryPath/CSS/QueryPathEventHandlerTest.php index fb025230..c5252a4a 100644 --- a/tests/QueryPath/CSS/QueryPathEventHandlerTest.php +++ b/tests/QueryPath/CSS/QueryPathEventHandlerTest.php @@ -714,6 +714,13 @@ public function testPseudoClassNthChild() $matches = $handler->getMatches(); $this->assertEquals(0, $matches->count()); + // Test nth-child(n+3) (3+ elements) + $handler = new QueryPathEventHandler($doc); + $handler->find('i:nth-child(n+3)'); + $matches = $handler->getMatches(); + $this->assertEquals(3, $matches->count()); + $this->assertEquals('four', $this->nthMatch($matches, 1)->getAttribute('id')); + // Test nth-child(-n+3) (First three lines) // $handler = new QueryPathEventHandler($doc); // $handler->find('i:nth-child(-n+3)'); From 4e2895f1f6462303979e851aa7103596a75b5a02 Mon Sep 17 00:00:00 2001 From: Dmitriy Zo Date: Wed, 6 Nov 2024 17:54:56 +0000 Subject: [PATCH 2/5] Use @dataProvider in testPseudoClassNthChild() to reduce code repetition --- CHANGELOG.md | 1 + .../CSS/QueryPathEventHandlerTest.php | 128 +++++------------- 2 files changed, 34 insertions(+), 95 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d248dc55..d66a9965 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ QueryPath Changelog # 4.0.2 - Fix for :nth-child(n+B) to select B-th and all following elements +- Update PHPUnit Test Suite to use @dataProvider in testPseudoClassNthChild() to reduce code repetition # 4.0.1 diff --git a/tests/QueryPath/CSS/QueryPathEventHandlerTest.php b/tests/QueryPath/CSS/QueryPathEventHandlerTest.php index c5252a4a..b0fb56e5 100644 --- a/tests/QueryPath/CSS/QueryPathEventHandlerTest.php +++ b/tests/QueryPath/CSS/QueryPathEventHandlerTest.php @@ -615,7 +615,31 @@ public function testChildAtIndex() { $this->assertEquals('one', $this->nthMatch($matches, 1)->getAttribute('id')); }*/ - public function testPseudoClassNthChild() + public function nthChildProvider(): array + { + return [ + [':root :even', 3, 'four' ], // full list + ['i:even', 2, 'four' ], // restricted to specific element + ['i:odd', 3, 'three' ], // restricted to specific element, odd this time + ['i:nth-child(odd)', 3, 'three' ], // odd + ['i:nth-child(2n+1)', 3, 'three' ], // odd, equiv to 2n + 1 + ['i:nth-child(2n-1)', 3, 'three' ], // odd, equiv to 2n + 1 + ['i:nth-child(2n)', 2, 'four' ], // even + //['i:nth-child(-2n)', 2, 'four' ], // Not totally sure what should be returned here + ['i:nth-child(4n)', 1, 'four', 0], // every fourth row + ['i:nth-child(4n+1)', 2, 'five' ], // first of every four rows + ['i:nth-child(1)', 1, 'one', 0 ], // first row + ['i:nth-child(0n-0)', 0, null ], // empty list + ['i:nth-child(n+3)', 3, 'four' ], // third+ lines + //['i:nth-child(0n+3)', 1, 'three' ], // third element in a group of siblings + //['i:nth-child(-n+3)', 3, 'three' ], // first three lines + ]; + } + + /** + * @dataProvider nthChildProvider + */ + public function testPseudoClassNthChild($selector, $matchesCount, $matchId, $matchIndex = 1) { $xml = ' @@ -631,102 +655,16 @@ public function testPseudoClassNthChild() // Test full list $handler = new QueryPathEventHandler($doc); - $handler->find(':root :even'); - $matches = $handler->getMatches(); - $this->assertEquals(3, $matches->count()); - $this->assertEquals('four', $this->nthMatch($matches, 1)->getAttribute('id')); - - // Test restricted to specific element - $handler = new QueryPathEventHandler($doc); - $handler->find('i:even'); - $matches = $handler->getMatches(); - $this->assertEquals(2, $matches->count()); - $this->assertEquals('four', $this->nthMatch($matches, 1)->getAttribute('id')); - - // Test restricted to specific element, odd this time - $handler = new QueryPathEventHandler($doc); - $handler->find('i:odd'); - $matches = $handler->getMatches(); - $this->assertEquals(3, $matches->count()); - $this->assertEquals('three', $this->nthMatch($matches, 1)->getAttribute('id')); - - // Test nth-child(odd) - $handler = new QueryPathEventHandler($doc); - $handler->find('i:nth-child(odd)'); - $matches = $handler->getMatches(); - $this->assertEquals(3, $matches->count()); - $this->assertEquals('three', $this->nthMatch($matches, 1)->getAttribute('id')); - - // Test nth-child(2n+1) - $handler = new QueryPathEventHandler($doc); - $handler->find('i:nth-child(2n+1)'); - $matches = $handler->getMatches(); - $this->assertEquals(3, $matches->count()); - $this->assertEquals('three', $this->nthMatch($matches, 1)->getAttribute('id')); - - // Test nth-child(2n) (even) - $handler = new QueryPathEventHandler($doc); - $handler->find('i:nth-child(2n)'); - $matches = $handler->getMatches(); - $this->assertEquals(2, $matches->count()); - $this->assertEquals('four', $this->nthMatch($matches, 1)->getAttribute('id')); - - // Not totally sure what should be returned here - // Test nth-child(-2n) - // $handler = new QueryPathEventHandler($doc); - // $handler->find('i:nth-child(-2n)'); - // $matches = $handler->getMatches(); - // $this->assertEquals(2, $matches->count()); - // $this->assertEquals('four', $this->nthMatch($matches, 1)->getAttribute('id')); - - // Test nth-child(2n-1) (odd, equiv to 2n + 1) - $handler = new QueryPathEventHandler($doc); - $handler->find('i:nth-child(2n-1)'); - $matches = $handler->getMatches(); - $this->assertEquals(3, $matches->count()); - $this->assertEquals('three', $this->nthMatch($matches, 1)->getAttribute('id')); - - // Test nth-child(4n) (every fourth row) - $handler = new QueryPathEventHandler($doc); - $handler->find('i:nth-child(4n)'); + $handler->find($selector); $matches = $handler->getMatches(); - $this->assertEquals(1, $matches->count()); - $this->assertEquals('four', $this->nthMatch($matches, 0)->getAttribute('id')); - - // Test nth-child(4n+1) (first of every four rows) - $handler = new QueryPathEventHandler($doc); - $handler->find('i:nth-child(4n+1)'); - $matches = $handler->getMatches(); - // Should match rows one and five - $this->assertEquals(2, $matches->count()); - $this->assertEquals('five', $this->nthMatch($matches, 1)->getAttribute('id')); - - // Test nth-child(1) (First row) - $handler = new QueryPathEventHandler($doc); - $handler->find('i:nth-child(1)'); - $matches = $handler->getMatches(); - $this->assertEquals(1, $matches->count()); - $this->assertEquals('one', $this->firstMatch($matches)->getAttribute('id')); - - // Test nth-child(0n-0) (Empty list) - $handler = new QueryPathEventHandler($doc); - $handler->find('i:nth-child(0n-0)'); - $matches = $handler->getMatches(); - $this->assertEquals(0, $matches->count()); - - // Test nth-child(n+3) (3+ elements) - $handler = new QueryPathEventHandler($doc); - $handler->find('i:nth-child(n+3)'); - $matches = $handler->getMatches(); - $this->assertEquals(3, $matches->count()); - $this->assertEquals('four', $this->nthMatch($matches, 1)->getAttribute('id')); - - // Test nth-child(-n+3) (First three lines) - // $handler = new QueryPathEventHandler($doc); - // $handler->find('i:nth-child(-n+3)'); - // $matches = $handler->getMatches(); - // $this->assertEquals(3, $matches->count()); + $this->assertEquals($matchesCount, $matches->count()); + if ($matchesCount) { + $this->assertEquals($matchId, $this->nthMatch($matches, $matchIndex)->getAttribute('id')); + } + } + public function testPseudoClassNthChildNested() + { $xml = ' From aab738e85d2916bd28bd097cda8a06f4e8febb9b Mon Sep 17 00:00:00 2001 From: Dmitriy Zo Date: Wed, 6 Nov 2024 18:23:19 +0000 Subject: [PATCH 3/5] Fix for :nth-child(-n+B) to select first B elements --- CHANGELOG.md | 1 + tests/QueryPath/CSS/QueryPathEventHandlerTest.php | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d66a9965..e27d92b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ QueryPath Changelog # 4.0.2 - Fix for :nth-child(n+B) to select B-th and all following elements +- Fix for :nth-child(-n+B) to select first B elements - Update PHPUnit Test Suite to use @dataProvider in testPseudoClassNthChild() to reduce code repetition # 4.0.1 diff --git a/tests/QueryPath/CSS/QueryPathEventHandlerTest.php b/tests/QueryPath/CSS/QueryPathEventHandlerTest.php index b0fb56e5..0d79cb87 100644 --- a/tests/QueryPath/CSS/QueryPathEventHandlerTest.php +++ b/tests/QueryPath/CSS/QueryPathEventHandlerTest.php @@ -628,11 +628,12 @@ public function nthChildProvider(): array //['i:nth-child(-2n)', 2, 'four' ], // Not totally sure what should be returned here ['i:nth-child(4n)', 1, 'four', 0], // every fourth row ['i:nth-child(4n+1)', 2, 'five' ], // first of every four rows - ['i:nth-child(1)', 1, 'one', 0 ], // first row + ['i:nth-child(1)', 1, 'one', 0], // first row ['i:nth-child(0n-0)', 0, null ], // empty list ['i:nth-child(n+3)', 3, 'four' ], // third+ lines - //['i:nth-child(0n+3)', 1, 'three' ], // third element in a group of siblings - //['i:nth-child(-n+3)', 3, 'three' ], // first three lines + ['i:nth-child(-n+3)', 3, 'two' ], // first three elements + ['i:nth-child(-n+4)', 4, 'two' ], // first four lines + ['i:nth-child(0n+2)', 1, 'two', 0], // second element in a group of siblings ]; } From fc2462ac9212782a6d9e1075c6bd0ea147231640 Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Thu, 7 Nov 2024 17:14:20 +1100 Subject: [PATCH 4/5] Fix commented -2n test and add new -2n+4 test --- .../CSS/QueryPathEventHandlerTest.php | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/tests/QueryPath/CSS/QueryPathEventHandlerTest.php b/tests/QueryPath/CSS/QueryPathEventHandlerTest.php index 0d79cb87..5443aead 100644 --- a/tests/QueryPath/CSS/QueryPathEventHandlerTest.php +++ b/tests/QueryPath/CSS/QueryPathEventHandlerTest.php @@ -618,22 +618,24 @@ public function testChildAtIndex() { public function nthChildProvider(): array { return [ - [':root :even', 3, 'four' ], // full list - ['i:even', 2, 'four' ], // restricted to specific element - ['i:odd', 3, 'three' ], // restricted to specific element, odd this time - ['i:nth-child(odd)', 3, 'three' ], // odd - ['i:nth-child(2n+1)', 3, 'three' ], // odd, equiv to 2n + 1 - ['i:nth-child(2n-1)', 3, 'three' ], // odd, equiv to 2n + 1 - ['i:nth-child(2n)', 2, 'four' ], // even - //['i:nth-child(-2n)', 2, 'four' ], // Not totally sure what should be returned here - ['i:nth-child(4n)', 1, 'four', 0], // every fourth row - ['i:nth-child(4n+1)', 2, 'five' ], // first of every four rows - ['i:nth-child(1)', 1, 'one', 0], // first row - ['i:nth-child(0n-0)', 0, null ], // empty list - ['i:nth-child(n+3)', 3, 'four' ], // third+ lines - ['i:nth-child(-n+3)', 3, 'two' ], // first three elements - ['i:nth-child(-n+4)', 4, 'two' ], // first four lines - ['i:nth-child(0n+2)', 1, 'two', 0], // second element in a group of siblings + [':root :even', 3, 'four' ], // full list + ['i:even', 2, 'four' ], // restricted to specific element + ['i:odd', 3, 'three' ], // restricted to specific element, odd this time + ['i:nth-child(odd)', 3, 'three' ], // odd + ['i:nth-child(2n+1)', 3, 'three' ], // odd, equiv to 2n + 1 + ['i:nth-child(2n-1)', 3, 'three' ], // odd, equiv to 2n + 1 + ['i:nth-child(2n)', 2, 'four' ], // even + ['i:nth-child(-2n)', 0, null ], // empty list (An+B will only accept positive results and without B this will always be negative) + ['i:nth-child(-2n+4)', 2, 'two', 0 ], // two and four only + ['i:nth-child(-2n+4)', 2, 'four', 1 ], // two and four only + ['i:nth-child(4n)', 1, 'four', 0 ], // every fourth row + ['i:nth-child(4n+1)', 2, 'five' ], // first of every four rows + ['i:nth-child(1)', 1, 'one', 0 ], // first row + ['i:nth-child(0n-0)', 0, null ], // empty list + ['i:nth-child(n+3)', 3, 'four' ], // third+ lines + ['i:nth-child(-n+3)', 3, 'two' ], // first three elements + ['i:nth-child(-n+4)', 4, 'two' ], // first four lines + ['i:nth-child(0n+2)', 1, 'two', 0 ], // second element in a group of siblings ]; } From bb27bf0ca26fa833d06456c001c36c43d86f86fd Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Thu, 7 Nov 2024 17:15:15 +1100 Subject: [PATCH 5/5] Merge changelog entry with 4.0.1 --- CHANGELOG.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e27d92b1..810f0c6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,13 @@ QueryPath Changelog =========================== -# 4.0.2 +# 4.0.1 +- Only define global functions qp(), htmlqp(), and html5qp() if they haven't been defined already. - Fix for :nth-child(n+B) to select B-th and all following elements - Fix for :nth-child(-n+B) to select first B elements - Update PHPUnit Test Suite to use @dataProvider in testPseudoClassNthChild() to reduce code repetition -# 4.0.1 - -- Only define global functions qp(), htmlqp(), and html5qp() if they haven't been defined already. - # 4.0.0 - Reverse logic in DomQuery::html5() so that DomQuery::html5() returns the content of the current match, and DomQuery::html5('') replaces the content of the current matches. This matches the existing logic used in DomQuery::html().