diff --git a/packages/framework/tests/Feature/Views/SidebarBrandViewTest.php b/packages/framework/tests/Feature/Views/SidebarBrandViewTest.php index 10b840bf5e4..03670822588 100644 --- a/packages/framework/tests/Feature/Views/SidebarBrandViewTest.php +++ b/packages/framework/tests/Feature/Views/SidebarBrandViewTest.php @@ -16,7 +16,7 @@ class SidebarBrandViewTest extends TestCase public function testSidebarBrandView() { - $view = $this->test(view('hyde::components.docs.sidebar-brand')); + $view = $this->view(view('hyde::components.docs.sidebar-brand')); $view->assertSee('HydePHP Docs'); $view->assertSee('theme-toggle-button'); @@ -27,7 +27,7 @@ public function testSidebarBrandViewWithHomeRoute() { Hyde::routes()->addRoute((new DocumentationPage('index'))->getRoute()); - $view = $this->test(view('hyde::components.docs.sidebar-brand')); + $view = $this->view(view('hyde::components.docs.sidebar-brand')); $view->assertSee('HydePHP Docs'); $view->assertSee('theme-toggle-button'); @@ -38,7 +38,7 @@ public function testSidebarBrandViewWithDefaultHeaderText() { config(['docs.sidebar' => []]); - $view = $this->test(view('hyde::components.docs.sidebar-brand')); + $view = $this->view(view('hyde::components.docs.sidebar-brand')); $view->assertSee('Documentation'); $view->assertDontSee('HydePHP Docs'); @@ -50,7 +50,7 @@ public function testSidebarBrandViewWithDefaultHeaderTextAndHomeRoute() config(['docs.sidebar' => []]); - $view = $this->test(view('hyde::components.docs.sidebar-brand')); + $view = $this->view(view('hyde::components.docs.sidebar-brand')); $view->assertSee('Documentation'); $view->assertSeeHtml('Documentation', true); @@ -63,7 +63,7 @@ public function testSidebarBrandViewWithoutDarkmodeFeature() $mock->shouldReceive('hasFeature')->with('darkmode')->andReturn(false); HydeKernel::setInstance($mock); - $view = $this->test(view('hyde::components.docs.sidebar-brand')); + $view = $this->view(view('hyde::components.docs.sidebar-brand')); $view->assertSee('HydePHP Docs'); $view->assertDontSee('theme-toggle-button'); diff --git a/packages/framework/tests/Feature/Views/SidebarFooterTextViewTest.php b/packages/framework/tests/Feature/Views/SidebarFooterTextViewTest.php index e4d5c21e9a3..0b27fa6f467 100644 --- a/packages/framework/tests/Feature/Views/SidebarFooterTextViewTest.php +++ b/packages/framework/tests/Feature/Views/SidebarFooterTextViewTest.php @@ -14,7 +14,7 @@ class SidebarFooterTextViewTest extends TestCase public function testSidebarFooterTextViewWithDefaultConfig() { - $view = $this->test(view('hyde::components.docs.sidebar-footer-text')); + $view = $this->view(view('hyde::components.docs.sidebar-footer-text')); $view->assertSeeHtml('Back to home page'); } @@ -23,7 +23,7 @@ public function testSidebarFooterTextViewWhenConfigOptionIsTrue() { Config::set('docs.sidebar.footer', true); - $view = $this->test(view('hyde::components.docs.sidebar-footer-text')); + $view = $this->view(view('hyde::components.docs.sidebar-footer-text')); $view->assertSeeHtml('Back to home page'); } @@ -32,7 +32,7 @@ public function testSidebarFooterTextViewWhenConfigOptionIsMarkdownString() { Config::set('docs.sidebar.footer', 'Your Markdown String Here'); - $view = $this->test(view('hyde::components.docs.sidebar-footer-text')); + $view = $this->view(view('hyde::components.docs.sidebar-footer-text')); $view->assertSeeText('Your Markdown String Here'); } diff --git a/packages/framework/tests/Unit/HtmlTestingSupportMetaTest.php b/packages/framework/tests/Unit/HtmlTestingSupportMetaTest.php index 4d7a92a5cc7..5b6ec6f42da 100644 --- a/packages/framework/tests/Unit/HtmlTestingSupportMetaTest.php +++ b/packages/framework/tests/Unit/HtmlTestingSupportMetaTest.php @@ -1,5 +1,7 @@ assertSame([], $this->html('
Foo
')->getRootElement()->attributes); - /** @noinspection HtmlUnknownAttribute */ $this->assertSame([ 'name' => 'test', 'foo' => 'bar', @@ -429,21 +431,79 @@ public function testToArrayWithChildren() public function testToArrayWithAttributes() { - /** @noinspection HtmlUnknownAttribute */ $this->assertSame( ['id' => 'id', 'tag' => 'div', 'text' => 'Bar', 'classes' => ['class'], 'attributes' => ['name' => 'name']], $this->html('
Bar
')->getRootElement()->toArray() ); } + public function testElementAssertHasId() + { + $this->html('
Foo
')->getRootElement()->assertHasId('foo'); + } + + public function testElementAssertDoesNotHaveId() + { + $this->html('
Foo
')->getRootElement()->assertDoesNotHaveId('foo'); + } + public function testElementAssertHasClass() { - $this->html('
Foo
')->getRootElement()->hasClass('foo'); + $this->html('
Foo
')->getRootElement()->assertHasClass('foo'); } public function testElementAssertDoesNotHaveClass() { - $this->html('
Foo
')->getRootElement()->doesNotHaveClass('bar'); + $this->html('
Foo
')->getRootElement()->assertDoesNotHaveClass('bar'); + } + + public function testElementAssertHasAttribute() + { + $this->html('
Foo
')->getRootElement()->assertHasAttribute('name'); + } + + public function testElementAssertDoesNotHaveAttribute() + { + $this->html('
Foo
')->getRootElement()->assertDoesNotHaveAttribute('href'); + } + + public function testElementAssertHasAttributeWithValue() + { + $this->html('
Foo
')->getRootElement()->assertHasAttribute('name', 'foo'); + } + + public function testElementAssertHasAttributeWithWrongValue() + { + try { + $this->html('
Foo
')->getRootElement()->assertHasAttribute('name', 'bar'); + } catch (ExpectationFailedException $exception) { + $this->assertSame("The attribute 'name' did not have the expected value.\nFailed asserting that two strings are identical.", $exception->getMessage()); + } + } + + public function testElementAssertHasAttributeForwardsIdAssertions() + { + $this->html('
Foo
')->getRootElement()->assertHasAttribute('id', 'foo'); + } + + public function testElementAssertHasAttributeForwardsClassAssertions() + { + $this->html('
Foo
')->getRootElement()->assertHasAttribute('class', 'foo'); + } + + public function testAssertionCallsOnDocumentAreForwardedToRootElement() + { + $this->assertInstanceOf(TestableHtmlElement::class, + $this->html('
Foo
') + ->assertSee('Foo') + ->assertHasId('foo') + ->assertDoesNotHaveId('bar') + ->assertHasClass('bar') + ->assertDoesNotHaveClass('baz') + ->assertHasAttribute('class', 'bar') + ->assertDoesNotHaveAttribute('href') + ->assertSee('Foo') + ); } protected function exampleElement(): TestableHtmlElement diff --git a/packages/testing/src/Support/HtmlTesting/HtmlTestingAssertions.php b/packages/testing/src/Support/HtmlTesting/HtmlTestingAssertions.php index 351be0cf0c5..4b9da2ff53e 100644 --- a/packages/testing/src/Support/HtmlTesting/HtmlTestingAssertions.php +++ b/packages/testing/src/Support/HtmlTesting/HtmlTestingAssertions.php @@ -4,6 +4,7 @@ namespace Hyde\Testing\Support\HtmlTesting; +use Closure; use Illuminate\Testing\Assert as PHPUnit; trait HtmlTestingAssertions @@ -33,10 +34,70 @@ public function assertDontSeeEscaped(string $value): static return $this->doAssert(fn () => PHPUnit::assertStringNotContainsString(e($value), $this->html, "The escaped string '$value' was found in the HTML.")); } - protected function doAssert(callable $assertion): static + public function assertHasId(string $id): TestableHtmlElement + { + return $this->doElementAssert(fn () => PHPUnit::assertSame($id, $this->id, 'The id attribute did not have the expected value.')); + } + + public function assertDoesNotHaveId(string $id): TestableHtmlElement + { + return $this->doElementAssert(fn () => PHPUnit::assertNotSame($id, $this->id, 'The id attribute had the unexpected value.')); + } + + public function assertHasClass(string $class): TestableHtmlElement + { + return $this->doElementAssert(fn () => PHPUnit::assertContains($class, $this->classes, "The class '$class' was not found in the element.")); + } + + public function assertDoesNotHaveClass(string $class): TestableHtmlElement + { + return $this->doElementAssert(fn () => PHPUnit::assertNotContains($class, $this->classes, "The class '$class' was found in the element.")); + } + + public function assertHasAttribute(string $attribute, ?string $value = null): TestableHtmlElement + { + if ($attribute === 'id') { + return $this->assertHasId($value); + } + + if ($attribute === 'class') { + return $this->assertHasClass($value); + } + + $this->doElementAssert(fn () => PHPUnit::assertArrayHasKey($attribute, $this->attributes, "The attribute '$attribute' was not found in the element.")); + + if ($value) { + return $this->doElementAssert(fn () => PHPUnit::assertSame($value, $this->attributes[$attribute], "The attribute '$attribute' did not have the expected value.")); + } + + return $this; + } + + public function assertDoesNotHaveAttribute(string $attribute): TestableHtmlElement + { + return $this->doElementAssert(fn () => PHPUnit::assertArrayNotHasKey($attribute, $this->attributes, "The attribute '$attribute' was found in the element.")); + } + + /** @internal */ + public function doAssert(Closure $assertion): static { $assertion(); return $this; } + + protected function doElementAssert(Closure $assertion): TestableHtmlElement + { + // Proxy to the root element if we're a TestableHtmlDocument. + if ($this instanceof TestableHtmlDocument) { + $rootElement = $this->getRootElement(); + + // Bind closure to the root element. + $assertion = $assertion->bindTo($rootElement); + + return $rootElement->doAssert($assertion); + } + + return $this->doAssert($assertion); + } } diff --git a/packages/testing/src/Support/HtmlTesting/TestableHtmlElement.php b/packages/testing/src/Support/HtmlTesting/TestableHtmlElement.php index fd65429518c..cb647ec0ff9 100644 --- a/packages/testing/src/Support/HtmlTesting/TestableHtmlElement.php +++ b/packages/testing/src/Support/HtmlTesting/TestableHtmlElement.php @@ -8,10 +8,8 @@ use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Contracts\Support\Arrayable; -use Illuminate\Testing\Assert as PHPUnit; use function trim; -use function filled; use function strlen; use function explode; use function preg_match; @@ -61,7 +59,7 @@ public function __construct(string $html, DOMElement $element, ?TestableHtmlElem $this->attributes = $this->parseAttributes($element); } - /** @return array{id: ?string, tag: string, text: string, classes: ?array, attributes: ?array, nodes: \Illuminate\Support\Collection<\Hyde\Testing\Support\HtmlTesting\TestableHtmlElement>} */ + /** @return array{id: ?string, tag: string, text: string, classes: ?array, attributes: ?array, nodes: ?\Illuminate\Support\Collection<\Hyde\Testing\Support\HtmlTesting\TestableHtmlElement>} */ public function toArray(): array { return array_filter([ @@ -70,18 +68,8 @@ public function toArray(): array 'text' => $this->text, 'classes' => $this->classes, 'attributes' => $this->attributes, - 'nodes' => $this->nodes, - ], fn ($value): bool => filled($value)); - } - - public function hasClass(string $class): static - { - return $this->doAssert(fn () => PHPUnit::assertContains($class, $this->classes, "The class '$class' was not found in the element.")); - } - - public function doesNotHaveClass(string $class): static - { - return $this->doAssert(fn () => PHPUnit::assertNotContains($class, $this->classes, "The class '$class' was found in the element.")); + 'nodes' => $this->nodes->count() ? $this->nodes : null, + ]); } protected function parseTag(string $html): string diff --git a/packages/testing/src/TestsBladeViews.php b/packages/testing/src/TestsBladeViews.php index 01366963558..267932fa516 100644 --- a/packages/testing/src/TestsBladeViews.php +++ b/packages/testing/src/TestsBladeViews.php @@ -16,7 +16,7 @@ trait TestsBladeViews /** * Test a Blade view. */ - protected function test(string|View $view, $data = []): TestView + protected function view(string|View $view, $data = []): TestView { $data = array_merge($this->testViewData(), $data);