-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
304 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
namespace Neon; | ||
|
||
class View | ||
{ | ||
public function __construct(readonly private string $title) | ||
{ | ||
} | ||
|
||
public function html(): string | ||
{ | ||
return '<!DOCTYPE html>' . | ||
'<html>' . | ||
'<title>' . $this->title . '</title>' . | ||
'</html>'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
namespace Neon\Test\BaseFixture\Caught; | ||
|
||
use PHPUnit\Framework\AssertionFailedError; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
class CaughtTest extends TestCase | ||
{ | ||
/** | ||
* @test | ||
*/ | ||
public function caught(): void | ||
{ | ||
$exception = caught(function () { | ||
throw new \Exception('foo'); | ||
}); | ||
$this->assertSame('foo', $exception->getMessage()); | ||
} | ||
|
||
/** | ||
* @test | ||
*/ | ||
public function noException(): void | ||
{ | ||
try { | ||
caught(function () { | ||
}); | ||
} catch (AssertionFailedError $exception) { | ||
$this->assertSame('Failed to assert that exception is thrown.', $exception->getMessage()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
namespace Neon\Test\BaseFixture\Caught; | ||
|
||
use PHPUnit\Framework\AssertionFailedError; | ||
|
||
function caught(callable $block): \Throwable | ||
{ | ||
try { | ||
$block(); | ||
} catch (\Throwable $throwable) { | ||
return $throwable; | ||
} | ||
throw new AssertionFailedError('Failed to assert that exception is thrown.'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?php | ||
namespace Neon\Test\BaseFixture\View; | ||
|
||
readonly class ViewDom | ||
{ | ||
private \DOMDocument $document; | ||
private \DOMXPath $xPath; | ||
|
||
public function __construct(private string $html) | ||
{ | ||
$this->document = new \DOMDocument(); | ||
\libxml_use_internal_errors(true); | ||
$this->document->loadHTML($this->html); | ||
\libxml_clear_errors(); | ||
$this->xPath = new \DOMXPath($this->document); | ||
} | ||
|
||
public function find(string $xPath): string | ||
{ | ||
$first = $this->first($this->xPath->query($xPath), $xPath); | ||
return $first->textContent; | ||
} | ||
|
||
private function first(\DOMNodeList $nodes, string $xPath): \DOMElement|\DOMText | ||
{ | ||
$count = $nodes->count(); | ||
if ($count === 0) { | ||
throw $this->exception('Failed to find element', $xPath); | ||
} | ||
if ($count === 1) { | ||
return $nodes[0]; | ||
} | ||
throw $this->exception("Failed to find unique element (found $count)", $xPath); | ||
} | ||
|
||
private function exception(string $summary, string $xPath): \Exception | ||
{ | ||
$structure = new ViewDomStructure($this->document); | ||
return new \Exception("$summary: $xPath\n\n" . $structure->structure()); | ||
} | ||
|
||
public function docType(): string | ||
{ | ||
$node = $this->document->getRootNode(); | ||
return \strStr($this->document->saveHTML($node), "\n", true); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<?php | ||
namespace Neon\Test\BaseFixture\View; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use function Neon\Test\BaseFixture\Caught\caught; | ||
|
||
class ViewDomFindTest extends TestCase | ||
{ | ||
/** | ||
* @test | ||
*/ | ||
public function textContent(): void | ||
{ | ||
$dom = new ViewDom('<ul><li>Winter is coming</li><ul>'); | ||
$this->assertSame('Winter is coming', $dom->find('/html/body/ul/li')); | ||
} | ||
|
||
/** | ||
* @test | ||
*/ | ||
public function notFound(): void | ||
{ | ||
$dom = new ViewDom('<p>Missing</p>'); | ||
$throwable = caught(fn() => $dom->find('/html/body/ul/li')); | ||
$this->assertStringStartsWith('Failed to find element: /html/body/ul/li', $throwable->getMessage()); | ||
} | ||
|
||
/** | ||
* @test | ||
*/ | ||
public function many(): void | ||
{ | ||
$dom = new ViewDom('<p>One</p><p>Two</p>'); | ||
$throwable = caught(fn() => $dom->find('//p')); | ||
$this->assertStringStartsWith('Failed to find unique element (found 2): //p', $throwable->getMessage()); | ||
} | ||
|
||
/** | ||
* @test | ||
*/ | ||
public function textNode(): void | ||
{ | ||
$dom = new ViewDom('<p>We do not sow</p>'); | ||
$this->assertSame('We do not sow', $dom->find('//p/text()')); | ||
} | ||
|
||
/** | ||
* @test | ||
*/ | ||
public function htmlEntity(): void | ||
{ | ||
$dom = new ViewDom('<p>></p>'); | ||
$this->assertSame('>', $dom->find('//p/text()')); | ||
} | ||
|
||
/** | ||
* @test | ||
*/ | ||
public function structureNotFound(): void | ||
{ | ||
$throwable = $this->listItemException('<title>Foo</title> <p>Bar</p>'); | ||
$this->assertStringEndsWith( | ||
'html(head(title),body(p))', | ||
$throwable->getMessage()); | ||
} | ||
|
||
/** | ||
* @test | ||
*/ | ||
public function structureMany(): void | ||
{ | ||
$throwable = $this->listItemException('<ul><li>One</li><li>Two</li></ul>'); | ||
$this->assertStringEndsWith( | ||
'html(body(ul(li,li)))', | ||
$throwable->getMessage()); | ||
} | ||
|
||
private function listItemException(string $html): \Throwable | ||
{ | ||
return caught(fn() => (new ViewDom($html))->find('/html/body/ul/li')); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
namespace Neon\Test\BaseFixture\View; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
|
||
class ViewDomRootTest extends TestCase | ||
{ | ||
/** | ||
* @test | ||
*/ | ||
public function html5(): void | ||
{ | ||
$dom = new ViewDom('<!DOCTYPE html><html></html>'); | ||
$this->assertSame('<!DOCTYPE html>', $dom->docType()); | ||
} | ||
|
||
/** | ||
* @test | ||
*/ | ||
public function traditionalHtml(): void | ||
{ | ||
$dom = new ViewDom('<p>Foo</p>'); | ||
$this->assertSame( | ||
'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">', | ||
$dom->docType()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?php | ||
namespace Neon\Test\BaseFixture\View; | ||
|
||
readonly class ViewDomStructure | ||
{ | ||
public function __construct(private \DOMDocument $document) | ||
{ | ||
} | ||
|
||
public function structure(): string | ||
{ | ||
return $this->elementAsString($this->htmlNode()); | ||
} | ||
|
||
private function htmlNode(): \DOMElement | ||
{ | ||
return $this->document->getElementsByTagName('html')[0]; | ||
} | ||
|
||
private function elementAsString(\DOMElement $element): string | ||
{ | ||
$childNames = $this->childrenAsStrings($element); | ||
if (empty($childNames)) { | ||
return $element->nodeName; | ||
} | ||
$children = \implode(',', $childNames); | ||
return "{$element->nodeName}($children)"; | ||
} | ||
|
||
/** | ||
* @return string[] | ||
*/ | ||
private function childrenAsStrings(\DOMElement $element): array | ||
{ | ||
$childNames = []; | ||
foreach ($element->childNodes as $child) { | ||
/** @var \DOMNode $child */ | ||
if ($child->nodeType === \XML_ELEMENT_NODE) { | ||
$childNames[] = $this->elementAsString($child); | ||
} | ||
} | ||
return $childNames; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php | ||
namespace Neon\Test\Unit\View; | ||
|
||
use Neon\Test\BaseFixture\View\ViewDom; | ||
use Neon\View; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
class Test extends TestCase | ||
{ | ||
/** | ||
* @test | ||
*/ | ||
public function docType(): void | ||
{ | ||
$dom = new ViewDom($this->view('')); | ||
$this->assertSame('<!DOCTYPE html>', $dom->docType()); | ||
} | ||
|
||
/** | ||
* @test | ||
*/ | ||
public function title(): void | ||
{ | ||
$dom = new ViewDom($this->view('Winter is coming')); | ||
$this->assertSame( | ||
'Winter is coming', | ||
$dom->find('/html/head/title')); | ||
} | ||
|
||
private function view(string $title): string | ||
{ | ||
return (new View($title))->html(); | ||
} | ||
} |