diff --git a/src/Testing/ConvertTestResponseToTurboStreamCollection.php b/src/Testing/ConvertTestResponseToTurboStreamCollection.php index 5e241c3..6e19fa0 100644 --- a/src/Testing/ConvertTestResponseToTurboStreamCollection.php +++ b/src/Testing/ConvertTestResponseToTurboStreamCollection.php @@ -2,6 +2,7 @@ namespace Tonysm\TurboLaravel\Testing; +use DOMDocument; use Illuminate\Support\Collection; use Illuminate\Testing\TestResponse; @@ -9,10 +10,17 @@ class ConvertTestResponseToTurboStreamCollection { public function __invoke(TestResponse $response): Collection { - $parsed = simplexml_load_string(<<{$response->content()} - XML); + libxml_use_internal_errors(true); + $document = tap(new DOMDocument())->loadHTML($response->content()); + $elements = $document->getElementsByTagName('turbo-stream'); - return collect(json_decode(json_encode($parsed), true)['turbo-stream']); + $streams = collect(); + + /** @var \DOMElement $element */ + foreach ($elements as $element) { + $streams->push($element); + } + + return $streams; } } diff --git a/src/Testing/TurboStreamMatcher.php b/src/Testing/TurboStreamMatcher.php index 5809929..f073719 100644 --- a/src/Testing/TurboStreamMatcher.php +++ b/src/Testing/TurboStreamMatcher.php @@ -3,18 +3,18 @@ namespace Tonysm\TurboLaravel\Testing; use Closure; -use Illuminate\Http\Response; -use Illuminate\Support\Arr; -use Illuminate\Testing\TestResponse; +use DOMElement; use Illuminate\View\ComponentAttributeBag; +use PHPUnit\Framework\Assert; class TurboStreamMatcher { + /** @var \DOMElement */ private $turboStream; private array $wheres = []; private array $contents = []; - public function __construct($turboStream) + public function __construct(DOMElement $turboStream) { $this->turboStream = $turboStream; } @@ -70,9 +70,9 @@ public function attrs(): string private function matchesProps() { foreach ($this->wheres as $prop => $value) { - $actualProp = $this->turboStream['@attributes'][$prop] ?? false; + $propValue = $this->turboStream->getAttribute($prop); - if ($actualProp === false || $actualProp !== $value) { + if (! $propValue || $propValue !== $value) { return false; } } @@ -86,55 +86,27 @@ private function matchesContents() return true; } - // To assert that the Turbo Stream contains the desired text, we first need to - // rebuild the markup from the response. This is because we had to parse the - // HTML before getting here so we could assert each Turbo Stream separately. - - $content = new TestResponse(new Response($this->makeElements($this->turboStream['template'] ?? []))); - - foreach ($this->contents as $expectedContent) { - $content->assertSee($expectedContent); + foreach ($this->contents as $content) { + Assert::assertStringContainsString($content, $this->renderElement()); } return true; } - private function makeAttributes(array $attributes): string - { - return (new ComponentAttributeBag($attributes))->toHtml(); - } - - private function makeElements($tags) + private function renderElement(): string { - if (is_string($tags)) { - return $tags; - } - - $content = ''; - - foreach ($tags as $tag => $contents) { - $attrs = $this->makeAttributes($contents['@attributes'] ?? []); - - $strContent = $this->makeElements(is_array($contents) ? Arr::except($contents, '@attributes') : $contents); - $opening = trim(sprintf('%s %s', $tag, $attrs)); + $html = ''; + $children = $this->turboStream->childNodes; - if ($this->isSelfClosingTag($tag)) { - $content .= "<{$opening} />"; - } else { - $content .= "<{$opening}>{$strContent}"; - } + foreach ($children as $child) { + $html .= $child->ownerDocument->saveXML($child); } - return $content; + return $html; } - private function isSelfClosingTag(string $tag): bool + private function makeAttributes(array $attributes): string { - return in_array($tag, [ - 'input', - 'img', - 'br', - 'source', - ]); + return (new ComponentAttributeBag($attributes))->toHtml(); } }