From 705a1a61995c1119754f5bec072600fed704e999 Mon Sep 17 00:00:00 2001 From: Steve Grunwell <233836+stevegrunwell@users.noreply.github.com> Date: Sun, 3 Dec 2023 20:45:10 -0500 Subject: [PATCH] Embrace strict(er) types, ensure that #13 is covered by Selector --- src/Constraints/SelectorCount.php | 2 +- src/MarkupAssertionsTrait.php | 120 +++++++++++------- src/Selector.php | 6 +- tests/Integration/AssertionsTest.php | 4 +- .../Unit/Constraints/ContainsSelectorTest.php | 8 +- .../Constraints/ElementContainsStringTest.php | 22 ++-- .../Constraints/ElementMatchesRegExpTest.php | 16 +-- tests/Unit/Constraints/SelectorCountTest.php | 8 +- tests/Unit/DOMTest.php | 18 +-- tests/Unit/SelectorTest.php | 33 ++++- 10 files changed, 144 insertions(+), 93 deletions(-) diff --git a/src/Constraints/SelectorCount.php b/src/Constraints/SelectorCount.php index 013ff87..cf3e99e 100644 --- a/src/Constraints/SelectorCount.php +++ b/src/Constraints/SelectorCount.php @@ -49,7 +49,7 @@ public function toString(): string * Evaluates the constraint for parameter $other. Returns true if the * constraint is met, false otherwise. * - * @param mixed $other value or object to evaluate + * @param mixed $html value or object to evaluate * * @return bool */ diff --git a/src/MarkupAssertionsTrait.php b/src/MarkupAssertionsTrait.php index f622ad0..cf2b805 100644 --- a/src/MarkupAssertionsTrait.php +++ b/src/MarkupAssertionsTrait.php @@ -22,14 +22,17 @@ trait MarkupAssertionsTrait * * @since 1.0.0 * - * @param string $selector A query selector for the element to find. - * @param string $markup The output that should contain the $selector. - * @param string $message A message to display if the assertion fails. + * @param string|array $selector A query selector to search for. + * @param string $markup The output that should contain the $selector. + * @param string $message A message to display if the assertion fails. * * @return void */ - public function assertContainsSelector($selector, $markup = '', $message = '') - { + public function assertContainsSelector( + $selector, + string $markup = '', + string $message = '' + ): void { $constraint = new ContainsSelector(new Selector($selector)); static::assertThat($markup, $constraint, $message); @@ -40,14 +43,17 @@ public function assertContainsSelector($selector, $markup = '', $message = '') * * @since 1.0.0 * - * @param string $selector A query selector for the element to find. - * @param string $markup The output that should not contain the $selector. - * @param string $message A message to display if the assertion fails. + * @param string|array $selector A query selector to search for. + * @param string $markup The output that should not contain the $selector. + * @param string $message A message to display if the assertion fails. * * @return void */ - public function assertNotContainsSelector($selector, $markup = '', $message = '') - { + public function assertNotContainsSelector( + $selector, + string $markup = '', + string $message = '' + ): void { $constraint = new LogicalNot(new ContainsSelector(new Selector($selector))); static::assertThat($markup, $constraint, $message); @@ -58,15 +64,19 @@ public function assertNotContainsSelector($selector, $markup = '', $message = '' * * @since 1.0.0 * - * @param int $count The number of matching elements expected. - * @param string $selector A query selector for the element to find. - * @param string $markup The markup to run the assertion against. - * @param string $message A message to display if the assertion fails. + * @param int $count The number of matching elements expected. + * @param string|array $selector A query selector to search for. + * @param string $markup The markup to run the assertion against. + * @param string $message A message to display if the assertion fails. * * @return void */ - public function assertSelectorCount($count, $selector, $markup = '', $message = '') - { + public function assertSelectorCount( + int $count, + $selector, + string $markup = '', + string $message = '' + ): void { $constraint = new SelectorCount(new Selector($selector), $count); static::assertThat($markup, $constraint, $message); @@ -85,8 +95,11 @@ public function assertSelectorCount($count, $selector, $markup = '', $message = * * @return void */ - public function assertHasElementWithAttributes($attributes = [], $markup = '', $message = '') - { + public function assertHasElementWithAttributes( + array $attributes = [], + string $markup = '', + string $message = '' + ): void { $constraint = new ContainsSelector(new Selector($attributes)); static::assertThat($markup, $constraint, $message); @@ -105,8 +118,11 @@ public function assertHasElementWithAttributes($attributes = [], $markup = '', $ * * @return void */ - public function assertNotHasElementWithAttributes($attributes = [], $markup = '', $message = '') - { + public function assertNotHasElementWithAttributes( + $attributes = [], + $markup = '', + $message = '' + ): void { $constraint = new LogicalNot(new ContainsSelector(new Selector($attributes))); static::assertThat($markup, $constraint, $message); @@ -117,15 +133,19 @@ public function assertNotHasElementWithAttributes($attributes = [], $markup = '' * * @since 1.1.0 * - * @param string $contents The string to look for within the DOM node's contents. - * @param string $selector A query selector for the element to find. - * @param string $markup The output that should contain the $selector. - * @param string $message A message to display if the assertion fails. + * @param string $contents The string to look for within the DOM node's contents. + * @param string|array $selector A query selector to search for. + * @param string $markup The output that should contain the $selector. + * @param string $message A message to display if the assertion fails. * * @return void */ - public function assertElementContains($contents, $selector = '', $markup = '', $message = '') - { + public function assertElementContains( + string $contents, + $selector = '', + string $markup = '', + string $message = '' + ): void { $constraint = new ElementContainsString(new Selector($selector), $contents); static::assertThat($markup, $constraint, $message); @@ -136,15 +156,19 @@ public function assertElementContains($contents, $selector = '', $markup = '', $ * * @since 1.1.0 * - * @param string $contents The string to look for within the DOM node's contents. - * @param string $selector A query selector for the element to find. - * @param string $markup The output that should not contain the $selector. - * @param string $message A message to display if the assertion fails. + * @param string $contents The string to look for within the DOM node's contents. + * @param string|array $selector A query selector to search for. + * @param string $markup The output that should not contain the $selector. + * @param string $message A message to display if the assertion fails. * * @return void */ - public function assertElementNotContains($contents, $selector = '', $markup = '', $message = '') - { + public function assertElementNotContains( + string $contents, + $selector = '', + string $markup = '', + string $message = '' + ): void { $constraint = new LogicalNot(new ElementContainsString(new Selector($selector), $contents)); static::assertThat($markup, $constraint, $message); @@ -155,15 +179,19 @@ public function assertElementNotContains($contents, $selector = '', $markup = '' * * @since 1.1.0 * - * @param string $regexp The regular expression pattern to look for within the DOM node. - * @param string $selector A query selector for the element to find. - * @param string $markup The output that should contain the $selector. - * @param string $message A message to display if the assertion fails. + * @param string $regexp The regular expression pattern to look for within the DOM node. + * @param string|array $selector A query selector to search for. + * @param string $markup The output that should contain the $selector. + * @param string $message A message to display if the assertion fails. * * @return void */ - public function assertElementRegExp($regexp, $selector = '', $markup = '', $message = '') - { + public function assertElementRegExp( + string $regexp, + $selector = '', + string $markup = '', + string $message = '' + ): void { $constraint = new ElementMatchesRegExp(new Selector($selector), $regexp); static::assertThat($markup, $constraint, $message); @@ -174,15 +202,19 @@ public function assertElementRegExp($regexp, $selector = '', $markup = '', $mess * * @since 1.1.0 * - * @param string $regexp The regular expression pattern to look for within the DOM node. - * @param string $selector A query selector for the element to find. - * @param string $markup The output that should not contain the $selector. - * @param string $message A message to display if the assertion fails. + * @param string $regexp The regular expression pattern to look for within the DOM node. + * @param string|array $selector A query selector to search for. + * @param string $markup The output that should not contain the $selector. + * @param string $message A message to display if the assertion fails. * * @return void */ - public function assertElementNotRegExp($regexp, $selector = '', $markup = '', $message = '') - { + public function assertElementNotRegExp( + string $regexp, + $selector = '', + string $markup = '', + string $message = '' + ): void { $constraint = new LogicalNot(new ElementMatchesRegExp(new Selector($selector), $regexp)); static::assertThat($markup, $constraint, $message); diff --git a/src/Selector.php b/src/Selector.php index 0b0f671..f4a58c4 100644 --- a/src/Selector.php +++ b/src/Selector.php @@ -38,7 +38,7 @@ public function __construct($selector) * * @return string */ - public function __toString() + public function __toString(): string { return $this->getValue(); } @@ -48,7 +48,7 @@ public function __toString() * * @return string */ - public function getValue() + public function getValue(): string { return $this->selector; } @@ -61,7 +61,7 @@ public function getValue() * * @return string The flattened attribute array. */ - private function attributeArrayToString($attributes) + private function attributeArrayToString(array $attributes): string { if (empty($attributes)) { throw new AttributeArrayException('Attributes array is empty.'); diff --git a/tests/Integration/AssertionsTest.php b/tests/Integration/AssertionsTest.php index 77e9db0..c50c461 100644 --- a/tests/Integration/AssertionsTest.php +++ b/tests/Integration/AssertionsTest.php @@ -30,7 +30,7 @@ class MarkupAssertionsTraitTest extends TestCase HTML; - public function testPresenceOfSelectors() + public function testPresenceOfSelectors(): void { $this->assertContainsSelector('main', $this->markup); $this->assertContainsSelector('h1', $this->markup); @@ -57,7 +57,7 @@ public function testPresenceOfSelectors() ); } - public function testMatchingContentsOfSelectors() + public function testMatchingContentsOfSelectors(): void { $this->assertElementContains('Good news', 'main', $this->markup); $this->assertElementContains('Good news', 'h1', $this->markup); diff --git a/tests/Unit/Constraints/ContainsSelectorTest.php b/tests/Unit/Constraints/ContainsSelectorTest.php index 29103cb..2712756 100644 --- a/tests/Unit/Constraints/ContainsSelectorTest.php +++ b/tests/Unit/Constraints/ContainsSelectorTest.php @@ -19,7 +19,7 @@ class ContainsSelectorTest extends TestCase * @test * @dataProvider provideSelectorVariants */ - public function it_should_find_matching_selectors_in_content($selector) + public function it_should_find_matching_selectors_in_content(string $selector): void { $constraint = new ContainsSelector(new Selector($selector)); $html = 'Example'; @@ -31,7 +31,7 @@ public function it_should_find_matching_selectors_in_content($selector) * @test * @dataProvider provideSelectorVariants */ - public function it_should_not_find_unmatched_selectors_in_content($selector) + public function it_should_not_find_unmatched_selectors_in_content(string $selector): void { $constraint = new ContainsSelector(new Selector($selector)); $html = '

This element has little to do with the link.

'; @@ -42,7 +42,7 @@ public function it_should_not_find_unmatched_selectors_in_content($selector) /** * @test */ - public function it_should_fail_with_a_useful_error_message() + public function it_should_fail_with_a_useful_error_message(): void { $selector = new Selector('p'); $html = '

Some Title

'; @@ -65,7 +65,7 @@ public function it_should_fail_with_a_useful_error_message() * * @return iterable> */ - public function provideSelectorVariants() + public function provideSelectorVariants(): iterable { yield 'Simple tag name' => ['a']; yield 'Class name' => ['.link']; diff --git a/tests/Unit/Constraints/ElementContainsStringTest.php b/tests/Unit/Constraints/ElementContainsStringTest.php index 4a050a2..4f3a595 100644 --- a/tests/Unit/Constraints/ElementContainsStringTest.php +++ b/tests/Unit/Constraints/ElementContainsStringTest.php @@ -18,7 +18,7 @@ class ElementContainsStringTest extends TestCase /** * @test */ - public function it_should_match_a_string_in_the_given_markup() + public function it_should_match_a_string_in_the_given_markup(): void { $constraint = new ElementContainsString(new Selector('p'), 'This is the content', true); $html = '

Title

This is the content

'; @@ -29,7 +29,7 @@ public function it_should_match_a_string_in_the_given_markup() /** * @test */ - public function it_should_match_in_a_case_sensitive_manner_by_default() + public function it_should_match_in_a_case_sensitive_manner_by_default(): void { $constraint = new ElementContainsString(new Selector('p'), 'THIS IS THE CONTENT'); $html = '

Title

This is the content

'; @@ -43,7 +43,7 @@ public function it_should_match_in_a_case_sensitive_manner_by_default() /** * @test */ - public function it_should_be_able_to_ignore_case() + public function it_should_be_able_to_ignore_case(): void { $constraint = new ElementContainsString(new Selector('p'), 'THIS IS THE CONTENT', true); $html = '

Title

This is the content

'; @@ -57,7 +57,7 @@ public function it_should_be_able_to_ignore_case() /** * @test */ - public function it_should_fail_if_no_match_is_found() + public function it_should_fail_if_no_match_is_found(): void { $constraint = new ElementContainsString(new Selector('p'), 'This is the content'); $html = '

This is the content

But this is not

'; @@ -72,7 +72,7 @@ public function it_should_fail_if_no_match_is_found() * * @ticket https://github.com/stevegrunwell/phpunit-markup-assertions/issues/31 */ - public function it_should_be_able_to_handle_various_character_sets($greeting) + public function it_should_be_able_to_handle_various_character_sets(string $greeting): void { $constraint = new ElementContainsString(new Selector('h1'), $greeting); $html = sprintf('

%s

', $greeting); @@ -83,7 +83,7 @@ public function it_should_be_able_to_handle_various_character_sets($greeting) /** * @test */ - public function it_should_test_against_the_inner_contents_of_the_found_nodes() + public function it_should_test_against_the_inner_contents_of_the_found_nodes(): void { $constraint = new ElementContainsString(new Selector('p'), 'class'); $html = '

First

Second

'; @@ -97,7 +97,7 @@ public function it_should_test_against_the_inner_contents_of_the_found_nodes() /** * @test */ - public function it_should_fail_with_a_useful_error_message() + public function it_should_fail_with_a_useful_error_message(): void { $html = '

Some other string

'; $expected = <<<'MSG' @@ -121,7 +121,7 @@ public function it_should_fail_with_a_useful_error_message() /** * @test */ - public function it_should_include_all_relevant_matches_in_error_messages() + public function it_should_include_all_relevant_matches_in_error_messages(): void { $html = '

Some other string

Yet another string

'; $expected = <<<'MSG' @@ -146,7 +146,7 @@ public function it_should_include_all_relevant_matches_in_error_messages() /** * @test */ - public function it_should_provide_a_simple_error_message_if_no_selector_matches_are_found() + public function it_should_provide_a_simple_error_message_if_no_selector_matches_are_found(): void { $html = '

Some other string

Yet another string

'; $expected = "Failed asserting that any elements match selector 'h1'."; @@ -164,9 +164,9 @@ public function it_should_provide_a_simple_error_message_if_no_selector_matches_ /** * Provide a list of strings in various language. * - * @return Iterable> + * @return iterable> */ - public function provideGreetingsInDifferentLanguages() + public function provideGreetingsInDifferentLanguages(): iterable { yield 'Arabic' => ['مرحبا!']; yield 'Chinese' => ['你好']; diff --git a/tests/Unit/Constraints/ElementMatchesRegExpTest.php b/tests/Unit/Constraints/ElementMatchesRegExpTest.php index 3d88125..5315d95 100644 --- a/tests/Unit/Constraints/ElementMatchesRegExpTest.php +++ b/tests/Unit/Constraints/ElementMatchesRegExpTest.php @@ -18,7 +18,7 @@ class ElementMatchesRegExpTest extends TestCase /** * @test */ - public function it_should_match_a_pattern_in_the_given_markup() + public function it_should_match_a_pattern_in_the_given_markup(): void { $constraint = new ElementMatchesRegExp(new Selector('p'), '/\d+/'); $html = '

Title

12345

'; @@ -29,7 +29,7 @@ public function it_should_match_a_pattern_in_the_given_markup() /** * @test */ - public function it_should_respect_flags() + public function it_should_respect_flags(): void { $constraint = new ElementMatchesRegExp(new Selector('p'), '/[A-Z]+/i'); $html = '

Title

123hello456

'; @@ -40,7 +40,7 @@ public function it_should_respect_flags() /** * @test */ - public function it_should_fail_if_no_match_is_found() + public function it_should_fail_if_no_match_is_found(): void { $constraint = new ElementMatchesRegExp(new Selector('p'), '/[a-z]+/'); $html = '

Title

12345

'; @@ -54,7 +54,7 @@ public function it_should_fail_if_no_match_is_found() /** * @test */ - public function it_should_test_against_the_inner_contents_of_the_found_nodes() + public function it_should_test_against_the_inner_contents_of_the_found_nodes(): void { $constraint = new ElementMatchesRegExp(new Selector('p'), '/class/'); $html = '

First

Second

'; @@ -68,7 +68,7 @@ public function it_should_test_against_the_inner_contents_of_the_found_nodes() /** * @test */ - public function it_should_scope_queries_to_the_selector() + public function it_should_scope_queries_to_the_selector(): void { $constraint = new ElementMatchesRegExp(new Selector('h1'), '/\d+/'); $html = '

Title

12345

'; @@ -79,7 +79,7 @@ public function it_should_scope_queries_to_the_selector() /** * @test */ - public function it_should_fail_with_a_useful_error_message() + public function it_should_fail_with_a_useful_error_message(): void { $html = '

Some other string

'; $expected = <<<'MSG' @@ -103,7 +103,7 @@ public function it_should_fail_with_a_useful_error_message() /** * @test */ - public function it_should_include_all_relevant_matches_in_error_messages() + public function it_should_include_all_relevant_matches_in_error_messages(): void { $selector = new Selector('p'); $html = '

Some other string

Yet another string

'; @@ -129,7 +129,7 @@ public function it_should_include_all_relevant_matches_in_error_messages() /** * @test */ - public function it_should_provide_a_simple_error_message_if_no_selector_matches_are_found() + public function it_should_provide_a_simple_error_message_if_no_selector_matches_are_found(): void { $html = '

Some other string

Yet another string

'; $expected = "Failed asserting that any elements match selector 'h1'."; diff --git a/tests/Unit/Constraints/SelectorCountTest.php b/tests/Unit/Constraints/SelectorCountTest.php index 4edce40..026dc75 100644 --- a/tests/Unit/Constraints/SelectorCountTest.php +++ b/tests/Unit/Constraints/SelectorCountTest.php @@ -23,7 +23,7 @@ public function it_should_determine_if_the_expected_number_of_instances_are_pres string $markup, Selector $selector, int $expected - ) { + ): void { $constraint = new SelectorCount($selector, $expected); $this->assertTrue($constraint->evaluate($markup, '', true)); @@ -33,7 +33,7 @@ public function it_should_determine_if_the_expected_number_of_instances_are_pres * @test * @dataProvider provideMarkupVariants */ - public function it_should_fail_if_it_contains_a_different_number_of_matches() + public function it_should_fail_if_it_contains_a_different_number_of_matches(): void { $markup = '

This is the only paragraph

'; @@ -44,7 +44,7 @@ public function it_should_fail_if_it_contains_a_different_number_of_matches() /** * @test */ - public function it_should_fail_with_a_useful_error_message() + public function it_should_fail_with_a_useful_error_message(): void { $selector = new Selector('p.body'); $html = '

Some Title

'; @@ -67,7 +67,7 @@ public function it_should_fail_with_a_useful_error_message() * * @return iterable */ - public function provideMarkupVariants() + public function provideMarkupVariants(): iterable { yield 'Simple count' => [ '

This is a title

Content

', diff --git a/tests/Unit/DOMTest.php b/tests/Unit/DOMTest.php index 2bace0a..596b306 100644 --- a/tests/Unit/DOMTest.php +++ b/tests/Unit/DOMTest.php @@ -19,7 +19,7 @@ final class DOMTest extends TestCase * * @dataProvider provideMarkupWithInnerClass */ - public function it_should_be_able_to_count_selectors($markup, $expected) + public function it_should_be_able_to_count_selectors(string $markup, int $expected): void { $dom = new DOM($markup); $selector = new Selector('.inner'); @@ -31,7 +31,7 @@ public function it_should_be_able_to_count_selectors($markup, $expected) * @test * @testdox getInnerHtml() should retrieve the inner HTML for each matching element. */ - public function getInnerHtml_should_retrieve_the_inner_HTML_for_each_matching_element() + public function getInnerHtml_should_retrieve_the_inner_HTML_for_each_matching_element(): void { $markup = <<<'HTML'
    @@ -56,18 +56,18 @@ public function getInnerHtml_should_retrieve_the_inner_HTML_for_each_matching_el * @test * @testdox getInnerHtml() should return an empty array if there are no matches */ - public function getInnerHtml_should_return_an_empty_array_if_there_are_no_matches() + public function getInnerHtml_should_return_an_empty_array_if_there_are_no_matches(): void { $dom = new DOM('

    A title

    '); $this->assertEmpty($dom->getInnerHtml(new Selector('h2'))); } - /** + /** * @test * @testdox getOuterHtml() should retrieve the outer HTML for each matching element. */ - public function getOuterHtml_should_retrieve_the_outer_HTML_for_each_matching_element() + public function getOuterHtml_should_retrieve_the_outer_HTML_for_each_matching_element(): void { $markup = <<<'HTML'
      @@ -92,7 +92,7 @@ public function getOuterHtml_should_retrieve_the_outer_HTML_for_each_matching_el * @test * @testdox getOuterHtml() should return an empty array if there are no matches */ - public function getOuterHtml_should_return_an_empty_array_if_there_are_no_matches() + public function getOuterHtml_should_return_an_empty_array_if_there_are_no_matches(): void { $dom = new DOM('

      A title

      '); @@ -103,7 +103,7 @@ public function getOuterHtml_should_return_an_empty_array_if_there_are_no_matche * @test * @testdox query() should throw a SelectorException if the selector is invalid */ - public function query_should_throw_a_SelectorException_if_the_selector_is_invalid() + public function query_should_throw_a_SelectorException_if_the_selector_is_invalid(): void { $dom = new DOM('

      Some markup

      '); $selector = new Selector('#'); @@ -121,9 +121,9 @@ public function query_should_throw_a_SelectorException_if_the_selector_is_invali /** * Return test cases with varying numbers of .inner elements. * - * @return Iterable + * @return iterable */ - public function provideMarkupWithInnerClass() + public function provideMarkupWithInnerClass(): iterable { yield 'No matches' => [ '
      ', diff --git a/tests/Unit/SelectorTest.php b/tests/Unit/SelectorTest.php index 5ce0fc9..b5ed7d0 100644 --- a/tests/Unit/SelectorTest.php +++ b/tests/Unit/SelectorTest.php @@ -16,7 +16,7 @@ final class SelectorTest extends TestCase /** * @test */ - public function it_should_accept_string_selectors() + public function it_should_accept_string_selectors(): void { $selector = new Selector('a.some-class'); @@ -26,19 +26,38 @@ public function it_should_accept_string_selectors() /** * @test * + * @param array $attributes + * @param string $expected + * * @dataProvider provideAttributes */ - public function it_should_automatically_convert_attribute_arrays_to_strings($attributes, $expected) - { + public function it_should_automatically_convert_attribute_arrays_to_strings( + array $attributes, + string $expected + ): void { $selector = new Selector($attributes); $this->assertSame($expected, $selector->getValue()); } + /** + * @test + * + * @ticket https://github.com/stevegrunwell/phpunit-markup-assertions/issues/13 + */ + public function it_should_be_able_to_handle_spaces_in_attribute_values(): void + { + $selector = new Selector([ + 'data-attr' => 'foo bar baz', + ]); + + $this->assertSame('*[data-attr="foo bar baz"]', $selector->getValue()); + } + /** * @test */ - public function it_should_throw_if_unable_to_handle_attribute_array() + public function it_should_throw_if_unable_to_handle_attribute_array(): void { $this->expectException(AttributeArrayException::class); @@ -48,7 +67,7 @@ public function it_should_throw_if_unable_to_handle_attribute_array() /** * @test */ - public function it_should_be_able_to_be_cast_to_a_string() + public function it_should_be_able_to_be_cast_to_a_string(): void { $selector = new Selector('a.some-class'); @@ -58,9 +77,9 @@ public function it_should_be_able_to_be_cast_to_a_string() /** * Data provider for testFlattenAttributeArray(). * - * @return Iterable{array, string} The attribute array and teh expected string. + * @return iterable, string}> The attribute array and the expected string. */ - public function provideAttributes() + public function provideAttributes(): iterable { yield 'Single attribute' => [ [