started stubbing out HTML5 tags, added base test class for tag-specific tests
This commit is contained in:
parent
56658e5a47
commit
7f42a0cfca
18 changed files with 209 additions and 69 deletions
|
@ -1,9 +0,0 @@
|
|||
language: php
|
||||
php:
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
- 7.4
|
||||
install:
|
||||
- composer install
|
||||
script: composer test
|
|
@ -6,8 +6,5 @@ use ByJoby\HTML\Tags\AbstractContainerTag;
|
|||
|
||||
class BodyTag extends AbstractContainerTag implements BodyTagInterface
|
||||
{
|
||||
public function tag(): string
|
||||
{
|
||||
return 'body';
|
||||
}
|
||||
const TAG = 'body';
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ use ByJoby\HTML\Tags\AbstractContainerTag;
|
|||
|
||||
class HeadTag extends AbstractContainerTag implements HeadTagInterface
|
||||
{
|
||||
const TAG = 'head';
|
||||
|
||||
/** @var TitleTagInterface */
|
||||
protected $title;
|
||||
|
||||
|
@ -20,9 +22,4 @@ class HeadTag extends AbstractContainerTag implements HeadTagInterface
|
|||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function tag(): string
|
||||
{
|
||||
return 'head';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ use ByJoby\HTML\Tags\AbstractContainerTag;
|
|||
|
||||
class HtmlTag extends AbstractContainerTag implements HtmlTagInterface
|
||||
{
|
||||
const TAG = 'html';
|
||||
|
||||
/** @var HeadTagInterface */
|
||||
protected $head;
|
||||
/** @var BodyTagInterface */
|
||||
|
@ -26,11 +28,6 @@ class HtmlTag extends AbstractContainerTag implements HtmlTagInterface
|
|||
];
|
||||
}
|
||||
|
||||
public function tag(): string
|
||||
{
|
||||
return 'html';
|
||||
}
|
||||
|
||||
public function head(): HeadTagInterface
|
||||
{
|
||||
return $this->head;
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
namespace ByJoby\HTML\Containers\DocumentTags;
|
||||
|
||||
use ByJoby\HTML\Tags\AbstractContainerTag;
|
||||
use ByJoby\HTML\Traits\NodeTrait;
|
||||
use Exception;
|
||||
|
||||
class TitleTag implements TitleTagInterface
|
||||
{
|
||||
const TAG = 'title';
|
||||
|
||||
use NodeTrait;
|
||||
|
||||
/** @var string */
|
||||
|
|
|
@ -47,7 +47,7 @@ class Attributes implements IteratorAggregate, ArrayAccess
|
|||
function offsetGet(mixed $offset): mixed
|
||||
{
|
||||
$offset = static::sanitizeOffset($offset);
|
||||
return $this->array[$offset];
|
||||
return @$this->array[$offset];
|
||||
}
|
||||
|
||||
function offsetSet(mixed $offset, mixed $value): void
|
||||
|
|
9
src/Html5/Html5Document.php
Normal file
9
src/Html5/Html5Document.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace ByJoby\HTML\Html5;
|
||||
|
||||
use ByJoby\HTML\Containers\GenericHtmlDocument;
|
||||
|
||||
class Html5Document extends GenericHtmlDocument
|
||||
{
|
||||
}
|
44
src/Html5/Tags/BaseTag.php
Normal file
44
src/Html5/Tags/BaseTag.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace ByJoby\HTML\Html5\Tags;
|
||||
|
||||
use ByJoby\HTML\Tags\AbstractTag;
|
||||
|
||||
class BaseTag extends AbstractTag
|
||||
{
|
||||
const TAG = 'base';
|
||||
|
||||
public function href(): null|string
|
||||
{
|
||||
return $this->attributes()['href'];
|
||||
}
|
||||
|
||||
public function setHref(null|string $href): static
|
||||
{
|
||||
$this->attributes()['href'] = $href;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function unsetHref(): static
|
||||
{
|
||||
unset($this->attributes()['href']);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function target(): null|string
|
||||
{
|
||||
return $this->attributes()['target'];
|
||||
}
|
||||
|
||||
public function setTarget(null|string $target): static
|
||||
{
|
||||
$this->attributes()['target'] = $target;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function unsetTarget(): static
|
||||
{
|
||||
unset($this->attributes()['target']);
|
||||
return $this;
|
||||
}
|
||||
}
|
10
src/Html5/Tags/LinkTag.php
Normal file
10
src/Html5/Tags/LinkTag.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace ByJoby\HTML\Html5\Tags;
|
||||
|
||||
use ByJoby\HTML\Tags\AbstractTag;
|
||||
|
||||
class LinkTag extends AbstractTag
|
||||
{
|
||||
const TAG = 'link';
|
||||
}
|
10
src/Html5/Tags/MetaTag.php
Normal file
10
src/Html5/Tags/MetaTag.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace ByJoby\HTML\Html5\Tags;
|
||||
|
||||
use ByJoby\HTML\Tags\AbstractTag;
|
||||
|
||||
class MetaTag extends AbstractTag
|
||||
{
|
||||
const TAG = 'meta';
|
||||
}
|
11
src/Html5/Tags/StyleTag.php
Normal file
11
src/Html5/Tags/StyleTag.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace ByJoby\HTML\Html5\Tags;
|
||||
|
||||
use ByJoby\HTML\Tags\AbstractTag;
|
||||
|
||||
// TODO: implement some sort of AbstractContentTag to hold non-HTML content like styles (maybe also make the TitleTag use this)
|
||||
class StyleTag extends AbstractTag
|
||||
{
|
||||
const TAG = 'style';
|
||||
}
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace ByJoby\HTML\Tags;
|
||||
|
||||
use ByJoby\HTML\Helpers\Attributes;
|
||||
use ByJoby\HTML\Helpers\Classes;
|
||||
use ByJoby\HTML\Traits\ContainerMutableTrait;
|
||||
use ByJoby\HTML\Traits\ContainerTagTrait;
|
||||
use ByJoby\HTML\Traits\ContainerTrait;
|
||||
|
@ -17,4 +15,9 @@ abstract class AbstractContainerTag implements ContainerTagInterface
|
|||
use ContainerTagTrait {
|
||||
ContainerTagTrait::__toString insteadof TagTrait;
|
||||
}
|
||||
|
||||
public function tag(): string
|
||||
{
|
||||
return static::TAG; //@phpstan-ignore-line
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,15 @@
|
|||
|
||||
namespace ByJoby\HTML\Tags;
|
||||
|
||||
use ByJoby\HTML\Helpers\Attributes;
|
||||
use ByJoby\HTML\Helpers\Classes;
|
||||
use ByJoby\HTML\Traits\TagTrait;
|
||||
use ByJoby\HTML\Traits\NodeTrait;
|
||||
|
||||
abstract class AbstractTag implements TagInterface
|
||||
{
|
||||
use NodeTrait, TagTrait;
|
||||
|
||||
public function tag(): string
|
||||
{
|
||||
return static::TAG; //@phpstan-ignore-line
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,13 @@ use PHPUnit\Framework\TestCase;
|
|||
|
||||
class FragmentTest extends TestCase
|
||||
{
|
||||
public function tag(string $name): AbstractContainerTag
|
||||
{
|
||||
$tag = $this->getMockForAbstractClass(AbstractContainerTag::class, [], '', true, true, true, ['tag']);
|
||||
$tag->method('tag')->willReturn($name);
|
||||
return $tag;
|
||||
}
|
||||
|
||||
public function testConstruction()
|
||||
{
|
||||
$empty = new Fragment();
|
||||
|
@ -19,10 +26,8 @@ class FragmentTest extends TestCase
|
|||
public function testNestingDocument(): Fragment
|
||||
{
|
||||
$fragment = new Fragment();
|
||||
$div1 = $this->getMockForAbstractClass(AbstractContainerTag::class);
|
||||
$div1->method('tag')->will($this->returnValue('div'));
|
||||
$div2 = $this->getMockForAbstractClass(AbstractContainerTag::class);
|
||||
$div2->method('tag')->will($this->returnValue('div'));
|
||||
$div1 = $this->tag('div');
|
||||
$div2 = $this->tag('div');
|
||||
// adding div1 to fragment sets its fragment
|
||||
$fragment->addChild($div1);
|
||||
$this->assertEquals($fragment, $div1->document());
|
||||
|
@ -40,8 +45,7 @@ class FragmentTest extends TestCase
|
|||
/** @var AbstractContainerTag */
|
||||
$div2 = $div1->children()[0];
|
||||
// add a span and verify it has the right parent
|
||||
$span = $this->getMockForAbstractClass(AbstractTag::class);
|
||||
$span->method('tag')->will($this->returnValue('span'));
|
||||
$span = $this->tag('span');
|
||||
$div2->addChild($span);
|
||||
$this->assertEquals($fragment, $span->document());
|
||||
// detach and check document/parent of all nodes
|
||||
|
|
12
tests/Html5/Tags/BaseTagTest.php
Normal file
12
tests/Html5/Tags/BaseTagTest.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace ByJoby\HTML\Html5\Tags;
|
||||
|
||||
class BaseTagTest extends TagTestCase
|
||||
{
|
||||
public function testAttributeHelpers(): void
|
||||
{
|
||||
$this->assertAttributeHelperMethods('href', BaseTag::class);
|
||||
$this->assertAttributeHelperMethods('target', BaseTag::class);
|
||||
}
|
||||
}
|
46
tests/Html5/Tags/TagTestCase.php
Normal file
46
tests/Html5/Tags/TagTestCase.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace ByJoby\HTML\Html5\Tags;
|
||||
|
||||
use ByJoby\HTML\ContainerInterface;
|
||||
use ByJoby\HTML\Tags\TagInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
abstract class TagTestCase extends TestCase
|
||||
{
|
||||
protected function assertAttributeHelperMethods(string $attribute, string $class, string $test_value = 'some-value', string $render_value = 'some-value'): void
|
||||
{
|
||||
/** @var TagInterface */
|
||||
$tag = new $class;
|
||||
$words = explode('-', $attribute);
|
||||
$setFn = 'set' . implode('', array_map('ucfirst', $words));
|
||||
$unsetFn = 'unset' . implode('', array_map('ucfirst', $words));
|
||||
$getFn = array_shift($words) . implode('', array_map('ucfirst', $words));
|
||||
// test setter and expected rendering
|
||||
call_user_func([$tag, $setFn], $test_value);
|
||||
$this->assertTagRendersAttribute($tag, $attribute, $render_value);
|
||||
// test getter
|
||||
$this->assertEquals($render_value, call_user_func([$tag, $getFn]));
|
||||
$this->assertEquals($render_value, $tag->attributes()[$attribute]);
|
||||
// test unsetting via unset
|
||||
call_user_func([$tag,$unsetFn]);
|
||||
$this->assertNull(call_user_func([$tag,$getFn]));
|
||||
}
|
||||
|
||||
protected function assertTagRendersAttribute(TagInterface $tag, string $attribute, string $value)
|
||||
{
|
||||
if ($tag instanceof ContainerInterface) {
|
||||
$this->assertEquals(
|
||||
sprintf('<%s %s="%s"></%s>', $tag->tag(), $attribute, $value, $tag->tag()),
|
||||
$tag->__toString(),
|
||||
sprintf('Unexpected rendering of %s value %s is in %s tag', $attribute, $value, $tag->tag())
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
sprintf('<%s %s="%s"/>', $tag->tag(), $attribute, $value),
|
||||
$tag->__toString(),
|
||||
sprintf('Unexpected rendering of %s value %s is in %s tag', $attribute, $value, $tag->tag())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,21 +2,24 @@
|
|||
|
||||
namespace ByJoby\HTML\Tags;
|
||||
|
||||
use ByJoby\HTML\Helpers\Attributes;
|
||||
use ByJoby\HTML\Helpers\Classes;
|
||||
use ByJoby\HTML\Nodes\Text;
|
||||
use ByJoby\HTML\Nodes\UnsanitizedText;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AbstractContainerTagTest extends TestCase
|
||||
{
|
||||
public function tag(string $name): AbstractContainerTag
|
||||
{
|
||||
$tag = $this->getMockForAbstractClass(AbstractContainerTag::class, [], '', true, true, true, ['tag']);
|
||||
$tag->method('tag')->willReturn($name);
|
||||
return $tag;
|
||||
}
|
||||
|
||||
public function testDIV(): AbstractContainerTag
|
||||
{
|
||||
$div = $this->getMockForAbstractClass(AbstractContainerTag::class);
|
||||
$div->method('tag')->will($this->returnValue('div'));
|
||||
$div = $this->tag('div');
|
||||
$this->assertEquals('<div></div>', $div->__toString());
|
||||
$span = $this->getMockForAbstractClass(AbstractContainerTag::class);
|
||||
$span->method('tag')->will($this->returnValue('span'));
|
||||
$span = $this->tag('span');
|
||||
$div->addChild($span);
|
||||
$div->attributes()['a'] = 'b';
|
||||
$this->assertEquals(
|
||||
|
@ -34,9 +37,9 @@ class AbstractContainerTagTest extends TestCase
|
|||
/** @depends clone testDIV */
|
||||
public function testMoreNesting(AbstractContainerTag $div): AbstractContainerTag
|
||||
{
|
||||
/** @var AbstractContainerTag */
|
||||
$span1 = $div->children()[0];
|
||||
$span2 = $this->getMockForAbstractClass(AbstractContainerTag::class);
|
||||
$span2->method('tag')->will($this->returnValue('span'));
|
||||
$span2 = $this->tag('span');
|
||||
$span1->addChild($span2);
|
||||
$this->assertEquals(
|
||||
implode(PHP_EOL, [
|
||||
|
@ -54,8 +57,7 @@ class AbstractContainerTagTest extends TestCase
|
|||
/** @depends clone testDIV */
|
||||
public function testRemoveChild(AbstractContainerTag $div): void
|
||||
{
|
||||
$span2 = $this->getMockForAbstractClass(AbstractContainerTag::class);
|
||||
$span2->method('tag')->will($this->returnValue('span'));
|
||||
$span2 = $this->tag('span');
|
||||
// add a second span and remove it using its object
|
||||
$div->addChild($span2);
|
||||
$this->assertCount(2, $div->children());
|
||||
|
@ -80,38 +82,36 @@ class AbstractContainerTagTest extends TestCase
|
|||
/** @depends clone testMoreNesting */
|
||||
public function testDetach(AbstractContainerTag $div): void
|
||||
{
|
||||
$span1 = $div->children()[0];
|
||||
$span2 = $span1->children()[0];
|
||||
$span1->detach();
|
||||
/** @var AbstractContainerTag */
|
||||
$span = $div->children()[0];
|
||||
$span->detach();
|
||||
$this->assertEquals('<div a="b"></div>', $div->__toString());
|
||||
$this->assertNull($span1->parent());
|
||||
$this->assertNull($span->parent());
|
||||
}
|
||||
|
||||
/** @depends clone testMoreNesting */
|
||||
public function testDetachCopy(AbstractContainerTag $div): void
|
||||
{
|
||||
$span1 = $div->children()[0];
|
||||
$span2 = $span1->children()[0];
|
||||
$copy = $span1->detachCopy();
|
||||
/** @var AbstractContainerTag */
|
||||
$span = $div->children()[0];
|
||||
$copy = $span->detachCopy();
|
||||
$this->assertNull($copy->parent());
|
||||
$this->assertEquals($div, $span1->parent());
|
||||
$this->assertEquals($div, $span->parent());
|
||||
}
|
||||
|
||||
public function testAddChildBefore(): void
|
||||
{
|
||||
$div = $this->getMockForAbstractClass(AbstractContainerTag::class);
|
||||
$div->method('tag')->will($this->returnValue('div'));
|
||||
$div = $this->tag('div');
|
||||
// add a string child
|
||||
$div->addChild('a');
|
||||
$div->addChildBefore('b', 'a');
|
||||
$this->assertEquals('b', $div->children()[0]->__toString());
|
||||
// add an actual node object
|
||||
$span1 = $this->getMockForAbstractClass(AbstractContainerTag::class);
|
||||
$span1->method('tag')->will($this->returnValue('span'));
|
||||
$div->addChildBefore($span1, 'a');
|
||||
$this->assertEquals($span1, $div->children()[1]->__toString());
|
||||
$span = $this->tag('span');
|
||||
$div->addChildBefore($span, 'a');
|
||||
$this->assertEquals($span, $div->children()[1]->__toString());
|
||||
// add another object referencing the node object
|
||||
$div->addChildBefore('c', $span1);
|
||||
$div->addChildBefore('c', $span);
|
||||
$this->assertEquals('c', $div->children()[1]->__toString());
|
||||
// should throw an exception if reference child is not found
|
||||
$this->expectExceptionMessage('Reference child not found in this container');
|
||||
|
@ -120,19 +120,17 @@ class AbstractContainerTagTest extends TestCase
|
|||
|
||||
public function testAddChildAfter(): void
|
||||
{
|
||||
$div = $this->getMockForAbstractClass(AbstractContainerTag::class);
|
||||
$div->method('tag')->will($this->returnValue('div'));
|
||||
$div = $this->tag('div');
|
||||
// add a string child
|
||||
$div->addChild('a');
|
||||
$div->addChildAfter('b', 'a');
|
||||
$this->assertEquals('b', $div->children()[1]->__toString());
|
||||
// add an actual node object
|
||||
$span1 = $this->getMockForAbstractClass(AbstractContainerTag::class);
|
||||
$span1->method('tag')->will($this->returnValue('span'));
|
||||
$div->addChildAfter($span1, 'a');
|
||||
$this->assertEquals($span1, $div->children()[1]->__toString());
|
||||
$span = $this->tag('span');
|
||||
$div->addChildAfter($span, 'a');
|
||||
$this->assertEquals($span, $div->children()[1]->__toString());
|
||||
// add another object referencing the node object
|
||||
$div->addChildAfter('c', $span1);
|
||||
$div->addChildAfter('c', $span);
|
||||
$this->assertEquals('c', $div->children()[2]->__toString());
|
||||
// should throw an exception if reference child is not found
|
||||
$this->expectExceptionMessage('Reference child not found in this container');
|
||||
|
|
|
@ -8,10 +8,17 @@ use PHPUnit\Framework\TestCase;
|
|||
|
||||
class AbstractTagTest extends TestCase
|
||||
{
|
||||
|
||||
public function tag(string $name): AbstractTag
|
||||
{
|
||||
$tag = $this->getMockForAbstractClass(AbstractTag::class, [], '', true, true, true, ['tag']);
|
||||
$tag->method('tag')->willReturn($name);
|
||||
return $tag;
|
||||
}
|
||||
|
||||
public function testBR(): AbstractTag
|
||||
{
|
||||
$br = $this->getMockForAbstractClass(AbstractTag::class);
|
||||
$br->method('tag')->will($this->returnValue('br'));
|
||||
$br = $this->tag('br');
|
||||
$this->assertEquals('<br/>', $br->__toString());
|
||||
$this->assertInstanceOf(Classes::class, $br->classes());
|
||||
$this->assertInstanceOf(Attributes::class, $br->attributes());
|
||||
|
|
Loading…
Reference in a new issue