Skip to content

Commit

Permalink
Additional fix for :nth-child(n+B)
Browse files Browse the repository at this point in the history
  • Loading branch information
super-dm3 committed Nov 14, 2024
1 parent 49f95fb commit f150006
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 134 deletions.
1 change: 0 additions & 1 deletion src/CSS/DOMTraverser/PseudoClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,6 @@ protected function isNthChild($node, $value, $reverse = false, $byType = false):
$parent = $node->parentNode;
if (empty($parent)
|| ($groupSize === 0 && $elementInGroup === 0)
|| ($groupSize > 0 && $elementInGroup > $groupSize)
) {
return false;
}
Expand Down
2 changes: 2 additions & 0 deletions src/CSS/DOMTraverser/Util.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ public static function parseAnB($rule): array
$aVal = $matches[1] ?? 1;
if ($aVal === '-') {
$aVal = -1;
} elseif ($aVal === '') {
$aVal = 1;
} else {
$aVal = (int) $aVal;
}
Expand Down
168 changes: 35 additions & 133 deletions tests/QueryPath/CSS/PseudoClassTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,29 @@ public function testNthLastChild()
$this->assertEquals(3, $i);
}

public function testNthChild()
public function nthChildProvider(): array
{
return [
['2n+1', 10, ['a', 'c']], // Every odd row
['odd', 10, ['a', 'c']], // Every odd row
['2n', 10, ['b', 'd']], // Every even row
['even', 10, ['b', 'd']], // Even (2n)
['4n-1', 5, 'c' ], // 4n - 1 == 4n + 3
['6n-1', 3, null ], // 6n - 1
['26n-1', 0, null ], // 26n - 1
['0n+0', 0, null ], // 0n + 0 -- spec says this is always FALSE
['3', 1, 'c' ], // 3 (0n+3)
['-n+3', 3, null ], // -n+3: First three elements
['n+3', 18, null ], // third+ elements
['2n+4', 9, ['d', 'b']], // fourth+ even elements
['6n+30', 0, null ], // 6n + 30. These should always fail to match
];
}

/**
* @dataProvider nthChildProvider
*/
public function testNthChild($pattern, $matchesCount, $matchTag)
{
$xml = '<?xml version="1.0"?><root>';
$xml .= str_repeat('<a/><b/><c/><d/>', 5);
Expand All @@ -438,145 +460,25 @@ public function testNthChild()
[$ele, $root] = $this->doc($xml, 'root');
$nl = $root->childNodes;

// 2n + 1 -- Every odd row.
$i = 0;
$expects = ['a', 'c'];
$j = 0;
foreach ($nl as $n) {
$res = $ps->elementMatches('nth-child', $n, $root, '2n+1');
if ($res) {
++$i;
$name = $n->tagName;
$this->assertContains($name, $expects, sprintf('Expected b or d, got %s in slot %s', $name, ++$j));
}
}
$this->assertEquals(10, $i, '2n+1 is ten items.');

// Odd
$i = 0;
$expects = ['a', 'c'];
$j = 0;
foreach ($nl as $n) {
$res = $ps->elementMatches('nth-child', $n, $root, 'odd');
if ($res) {
++$i;
$name = $n->tagName;
$this->assertContains($name, $expects, sprintf('Expected b or d, got %s in slot %s', $name, ++$j));
}
}
$this->assertEquals(10, $i, '2n+1 is ten items.');

// 2n + 0 -- every even row
$i = 0;
$expects = ['b', 'd'];
foreach ($nl as $n) {
$res = $ps->elementMatches('nth-child', $n, $root, '2n');
if ($res) {
++$i;
$name = $n->tagName;
$this->assertContains($name, $expects, 'Expected a or c, got ' . $name);
}
}
$this->assertEquals(10, $i, '2n+0 is ten items.');

// Even (2n)
$i = 0;
$expects = ['b', 'd'];
foreach ($nl as $n) {
$res = $ps->elementMatches('nth-child', $n, $root, 'even');
if ($res) {
++$i;
$name = $n->tagName;
$this->assertContains($name, $expects, 'Expected a or c, got ' . $name);
}
}
$this->assertEquals(10, $i, ' even is ten items.');

// 4n - 1 == 4n + 3
$i = 0;
$j = 0;
foreach ($nl as $n) {
$res = $ps->elementMatches('nth-child', $n, $root, '4n-1');
$res = $ps->elementMatches('nth-child', $n, $root, $pattern);
if ($res) {
++$i;
$name = $n->tagName;
$this->assertEquals('c', $name, 'Expected c, got ' . $name);
}
}
$this->assertEquals(5, $i);

// 6n - 1
$i = 0;
foreach ($nl as $n) {
$res = $ps->elementMatches('nth-child', $n, $root, '6n-1');
if ($res) {
++$i;
}
}
$this->assertEquals(3, $i);

// 6n + 1
$i = 0;
foreach ($nl as $n) {
$res = $ps->elementMatches('nth-child', $n, $root, '6n+1');
if ($res) {
++$i;
}
}
$this->assertEquals(4, $i);

// 26n - 1
$i = 0;
foreach ($nl as $n) {
$res = $ps->elementMatches('nth-child', $n, $root, '26n-1');
if ($res) {
++$i;
}
}
$this->assertEquals(0, $i);

// 0n + 0 -- spec says this is always FALSE.
$i = 0;
foreach ($nl as $n) {
$res = $ps->elementMatches('nth-child', $n, $root, '0n+0');
if ($res) {
++$i;
}
}
$this->assertEquals(0, $i);

// 3 (0n+3)
$i = 0;
foreach ($nl as $n) {
$res = $ps->elementMatches('nth-child', $n, $root, '3');
if ($res) {
++$i;
$this->assertEquals('c', $n->tagName);
}
}
$this->assertEquals(1, $i);

// -n+3: First three elements.
$i = 0;
foreach ($nl as $n) {
$res = $ps->elementMatches('nth-child', $n, $root, '-n+3');
if ($res) {
++$i;
//$this->assertEquals('c', $n->tagName);
}
}
$this->assertEquals(3, $i);

// BROKEN RULES -- these should always fail to match.

// 6n + 7;
$i = 0;
foreach ($nl as $n) {
$res = $ps->elementMatches('nth-child', $n, $root, '6n+7');
if ($res) {
++$i;
if (is_string($matchTag)) {
$this->assertEquals($matchTag, $name, 'Invalid tagName match');
} elseif (is_array($matchTag)) {
$this->assertContains(
$name,
$matchTag,
'Expected only ['.implode(', ', $matchTag).'] tags, got '.$name.' in slot '.++$j
);
}
}
}
$this->assertEquals(0, $i);
$this->assertEquals($matchesCount, $i, 'Invalid matches count');
}

public function testEven()
Expand Down
1 change: 1 addition & 0 deletions tests/QueryPath/CSS/UtilTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public function testParseAnB()
$this->assertEquals([2, -1], Util::parseAnB('2n - 1'));
// -n + 3
$this->assertEquals([-1, 3], Util::parseAnB('-n+3'));
$this->assertEquals([1, 3], Util::parseAnB('n+3'));

// Test invalid values
$this->assertEquals([0, 0], Util::parseAnB('obviously + invalid'));
Expand Down

0 comments on commit f150006

Please sign in to comment.