diff --git a/tests/Unit/Property/SelectorTest.php b/tests/Unit/Property/SelectorTest.php index 13d4db62..c18915c0 100644 --- a/tests/Unit/Property/SelectorTest.php +++ b/tests/Unit/Property/SelectorTest.php @@ -78,6 +78,28 @@ public static function provideSelectorsAndSpecificities(): array ]; } + /** + * @return array + */ + public static function provideSelectorsWithEscapedQuotes(): array + { + return [ + 'escaped double quote in double-quoted attribute' => ['a[href="test\\"value"]'], + 'escaped single quote in single-quoted attribute' => ['a[href=\'test\\\'value\']'], + 'multiple escaped double quotes in double-quoted attribute' => ['a[title="say \\"hello\\" world"]'], + 'multiple escaped single quotes in single-quoted attribute' => ['a[title=\'say \\\'hello\\\' world\']'], + 'escaped quote at start of attribute value' => ['a[data-test="\\"start"]'], + 'escaped quote at end of attribute value' => ['a[data-test="end\\""]'], + 'escaped backslash followed by quote' => ['a[data-test="test\\\\"]'], + 'escaped backslash before escaped quote' => ['a[data-test="test\\\\\\"value"]'], + 'triple backslash before quote' => ['a[data-test="test\\\\\\""]'], + 'escaped single quotes in selector itself, with other escaped characters' + => ['.before\\:content-\\[\\\'\\\'\\]:before'], + 'escaped double quotes in selector itself, with other escaped characters' + => ['.before\\:content-\\[\\"\\"\\]:before'], + ]; + } + /** * @test * @@ -94,6 +116,45 @@ public function parsesValidSelector(string $selector): void self::assertSame($selector, $result->getSelector()); } + /** + * @test + */ + public function parsingAttributeWithEscapedQuoteDoesNotPrematurelyCloseString(): void + { + $selector = 'input[placeholder="Enter \\"quoted\\" text here"]'; + + $result = Selector::parse(new ParserState($selector, Settings::create())); + + self::assertInstanceOf(Selector::class, $result); + self::assertSame($selector, $result->getSelector()); + } + + /** + * @test + */ + public function parseDistinguishesEscapedFromUnescapedQuotes(): void + { + // One backslash = escaped quote (should not close string) + $selector = 'a[data-value="test\\"more"]'; + + $result = Selector::parse(new ParserState($selector, Settings::create())); + + self::assertSame($selector, $result->getSelector()); + } + + /** + * @test + */ + public function parseHandlesEvenNumberOfBackslashesBeforeQuote(): void + { + // Two backslashes = escaped backslash + unescaped quote (should close string) + $selector = 'a[data-value="test\\\\"]'; + + $result = Selector::parse(new ParserState($selector, Settings::create())); + + self::assertSame($selector, $result->getSelector()); + } + /** * @return array */ @@ -371,65 +432,4 @@ public function getArrayRepresentationThrowsException(): void $subject->getArrayRepresentation(); } - - /** - * @return array - */ - public static function provideSelectorsWithEscapedQuotes(): array - { - return [ - 'escaped double quote in double-quoted attribute' => ['a[href="test\\"value"]'], - 'escaped single quote in single-quoted attribute' => ['a[href=\'test\\\'value\']'], - 'multiple escaped double quotes in double-quoted attribute' => ['a[title="say \\"hello\\" world"]'], - 'multiple escaped single quotes in single-quoted attribute' => ['a[title=\'say \\\'hello\\\' world\']'], - 'escaped quote at start of attribute value' => ['a[data-test="\\"start"]'], - 'escaped quote at end of attribute value' => ['a[data-test="end\\""]'], - 'escaped backslash followed by quote' => ['a[data-test="test\\\\"]'], - 'escaped backslash before escaped quote' => ['a[data-test="test\\\\\\"value"]'], - 'triple backslash before quote' => ['a[data-test="test\\\\\\""]'], - 'escaped single quotes in selector itself, with other escaped characters' - => ['.before\\:content-\\[\\\'\\\'\\]:before'], - 'escaped double quotes in selector itself, with other escaped characters' - => ['.before\\:content-\\[\\"\\"\\]:before'], - ]; - } - - /** - * @test - */ - public function parsingAttributeWithEscapedQuoteDoesNotPrematurelyCloseString(): void - { - $selector = 'input[placeholder="Enter \\"quoted\\" text here"]'; - - $result = Selector::parse(new ParserState($selector, Settings::create())); - - self::assertInstanceOf(Selector::class, $result); - self::assertSame($selector, $result->getSelector()); - } - - /** - * @test - */ - public function parseDistinguishesEscapedFromUnescapedQuotes(): void - { - // One backslash = escaped quote (should not close string) - $selector = 'a[data-value="test\\"more"]'; - - $result = Selector::parse(new ParserState($selector, Settings::create())); - - self::assertSame($selector, $result->getSelector()); - } - - /** - * @test - */ - public function parseHandlesEvenNumberOfBackslashesBeforeQuote(): void - { - // Two backslashes = escaped backslash + unescaped quote (should close string) - $selector = 'a[data-value="test\\\\"]'; - - $result = Selector::parse(new ParserState($selector, Settings::create())); - - self::assertSame($selector, $result->getSelector()); - } }