Skip to content
Snippets Groups Projects
Unverified Commit 3dab365f authored by Thomas Flori's avatar Thomas Flori
Browse files

add tests for factory and layouts

parent 5fd471b2
No related branches found
No related tags found
No related merge requests found
......@@ -126,24 +126,6 @@ class Factory
return new View($this, $viewLocator->getPath($name), $data, $layout);
}
/**
* Creates a view for $name with $data and renders it
*
* If $layout is given the view will be wrapped in $layout using the layout ViewLocator. You have to define a layout
* ViewLocator first.
*
* @param string $name
* @param array $data
* @param string $layout
* @return string
*/
public function render(string $name, array $data = [], string $layout = null): string
{
$view = $this->view($name, $data, $layout);
$content = $view->render();
return $content;
}
/**
* Execute $function with $arguments
*
......@@ -151,11 +133,11 @@ class Factory
* be executed if no helper is defined for this name.
*
* @param View $view
* @param string|callable $function
* @param string $function
* @param mixed ...$arguments
* @return mixed
*/
public function helper(View $view, $function, ...$arguments)
public function helper(View $view, string $function, ...$arguments)
{
if ($this->helperLocator->has($function)) {
$helper = $this->helperLocator->getHelper($function);
......@@ -166,6 +148,21 @@ class Factory
return call_user_func($function, ...$arguments);
}
throw new \LogicException('$function has to be callable or a registered view helper');
throw new \Exception('$function has to be callable or a registered view helper');
}
/**
* Alias for ->view()->render()
*
* @param string $name
* @param array $data
* @param string $layout
* @return string
* @see Factory::view()
* @codeCoverageIgnore just an alias
*/
public function render(string $name, array $data = [], string $layout = null): string
{
return $this->view($name, $data, $layout)->render();
}
}
......@@ -60,6 +60,11 @@ class View
return $this->factory->helper($this, $name, ...$arguments);
}
public function __get($name)
{
return $this->section($name);
}
/**
* Add $data for the view
*
......@@ -217,7 +222,7 @@ class View
* @param string|null $alternative
* @return string
*/
public function section(string $name, string $alternative = ''): string
public function section(string $name, string $alternative = null): ?string
{
if (!isset($this->sections[$name])) {
return $alternative;
......
<?php
namespace Syna\Test;
use Syna\Factory;
use Mockery as m;
use Syna\HelperLocator;
use Syna\View;
use Syna\ViewHelper\CallableHelper;
use Syna\ViewLocator;
/**
* Class FactoryTest
*
* @package Syna\Test
* @author Thomas Flori <thflori@gmail.com>
* @covers \Syna\Factory
*/
class FactoryTest extends TestCase
{
/** @test */
public function usesTheViewLocatorToFindViews()
{
$viewLocator = m::mock(ViewLocator::class);
$factory = new Factory($viewLocator);
$viewLocator->shouldReceive('has')->with('viewName')
->once()->ordered()->andReturn(true);
$viewLocator->shouldReceive('getPath')->with('viewName')
->once()->ordered()->andReturn('/any/path');
$view = $factory->view('viewName');
self::assertSame('/any/path', self::accessProtected($view, 'path'));
}
/** @test */
public function usesTheLayoutLocatorToFindLayouts()
{
$layoutLocator = m::mock(ViewLocator::class);
$factory = new Factory(new ViewLocator($this->templatePath), null, $layoutLocator);
$this->createTemplate('viewName.php', '');
$layoutLocator->shouldReceive('has')->with('layoutName')
->once()->ordered()->andReturn(true);
$layoutLocator->shouldReceive('getPath')->with('layoutName')
->once()->ordered()->andReturn('/any/path');
$factory->view('viewName', [], 'layoutName');
}
/** @test */
public function namedLocatorsCanBeDefinedAndUsed()
{
$mailLocator = m::mock(ViewLocator::class);
$factory = new Factory(new ViewLocator($this->templatePath));
$mailLocator->shouldReceive('has')->with('viewName')
->once()->ordered()->andReturn(true);
$mailLocator->shouldReceive('getPath')->with('viewName')
->once()->ordered()->andReturn('/any/path');
$factory->addLocator('mail', $mailLocator);
$factory->view('mail::viewName');
}
/** @test */
public function throwsWhenALocatorIsNotDefined()
{
$factory = new Factory(new ViewLocator($this->templatePath));
self::expectException(\LogicException::class);
self::expectExceptionMessage('No locator for mail defined');
$factory->view('mail::viewName');
}
/** @test */
public function throwsWhenViewDoesNotExist()
{
$viewLocator = m::mock(ViewLocator::class);
$factory = new Factory($viewLocator);
$viewLocator->shouldReceive('has')->with('viewName')
->once()->ordered()->andReturn(false);
self::expectException(\Exception::class);
self::expectExceptionMessage('View viewName not found');
$factory->view('viewName');
}
/** @test */
public function usesTheHelperLocatorToFindHelpers()
{
$helperLocator = m::mock(HelperLocator::class);
$factory = new Factory(new ViewLocator($this->templatePath), $helperLocator);
$view = new View($factory, '/any/path');
$helperLocator->shouldReceive('has')->with('helperName')
->once()->ordered()->andReturn(true);
$helperLocator->shouldReceive('getHelper')->with('helperName')
->once()->ordered()->andReturn(new CallableHelper('strtoupper'));
$result = $factory->helper($view, 'helperName', 'argument1');
self::assertSame('ARGUMENT1', $result);
}
/** @test */
public function usesACallable()
{
$helperLocator = m::mock(HelperLocator::class);
$factory = new Factory(new ViewLocator($this->templatePath), $helperLocator);
$view = new View($factory, '/any/path');
$helperLocator->shouldReceive('has')->with('strtoupper')
->once()->ordered()->andReturn(false);
$result = $factory->helper($view, 'strtoupper', 'argument1');
self::assertSame('ARGUMENT1', $result);
}
/** @test */
public function throwsWhenHelperNotFoundAndNotCallable()
{
$helperLocator = m::mock(HelperLocator::class);
$factory = new Factory(new ViewLocator($this->templatePath), $helperLocator);
$view = new View($factory, '/any/path');
$helperLocator->shouldReceive('has')->with('helperName')
->once()->ordered()->andReturn(false);
self::expectException(\Exception::class);
self::expectExceptionMessage('$function has to be callable or a registered view helper');
$factory->helper($view, 'helperName', 'argument1');
}
/** @test */
public function isAStorageForSharedData()
{
$factory = new Factory(new ViewLocator($this->templatePath));
$factory->addSharedData(['var1' => 'foo bar']);
self::assertSame(['var1' => 'foo bar'], $factory->getSharedData());
$factory->addSharedData(['var1' => 'Foo Bar', 'var2' => 'John Doe']);
self::assertSame(['var1' => 'Foo Bar', 'var2' => 'John Doe'], $factory->getSharedData());
}
/** @test */
public function retrieveStoredLocators()
{
$factory = new Factory(new ViewLocator($this->templatePath));
self::assertInstanceOf(ViewLocator::class, $factory->getLocator());
self::assertInstanceOf(HelperLocator::class, $factory->getHelperLocator());
$factory->addLocator('mail', $mailLocator = new ViewLocator($this->templatePath));
self::assertSame($mailLocator, $factory->getLocator('mail'));
}
}
......@@ -26,4 +26,11 @@ class TestCase extends MockeryTestCase
file_put_contents($path, $content);
return $path;
}
protected static function accessProtected($obj, $prop) {
$reflection = new \ReflectionClass($obj);
$property = $reflection->getProperty($prop);
$property->setAccessible(true);
return $property->getValue($obj);
}
}
......@@ -7,6 +7,13 @@ use Syna\Test\TestCase;
use Mockery as m;
use Syna\ViewLocator;
/**
* Class EscapingTest
*
* @package Syna\Test\View
* @author Thomas Flori <thflori@gmail.com>
* @covers \Syna\View
*/
class EscapingTest extends TestCase
{
/** @var m\Mock|Factory */
......
......@@ -7,6 +7,13 @@ use Syna\Test\TestCase;
use Mockery as m;
use Syna\ViewLocator;
/**
* Class ExtendingAndPartialsTest
*
* @package Syna\Test\View
* @author Thomas Flori <thflori@gmail.com>
* @covers \Syna\View
*/
class ExtendingAndPartialsTest extends TestCase
{
/** @var m\Mock|Factory */
......@@ -48,6 +55,76 @@ class ExtendingAndPartialsTest extends TestCase
self::assertSame('<h2>Hello World!</h2>', $result);
}
/** @test */
public function sectionsCanBeAccessedViaMagicGetter()
{
$this->createTemplate(
'parent.php',
'<h2><?= $v->title ?></h2>' . PHP_EOL .
'<div class="content"><?= $v->content ?></div>'
);
$this->createTemplate(
'child.php',
'<?php $v->extend("parent") ?>' . PHP_EOL .
'<p>Lorem ipsum</p>' . PHP_EOL .
'<?php $v->provide("title", "Hello World!") ?>'
);
$result = $this->factory->render('child');
self::assertSame(
'<h2>Hello World!</h2>' . PHP_EOL .
'<div class="content"><p>Lorem ipsum</p></div>',
$result
);
}
/** @test */
public function defineDefaultsUsingSectionMethod()
{
$this->createTemplate(
'parent.php',
'<h2><?= $v->section("title", "Foo") ?></h2>' . PHP_EOL .
'<div class="content"><?= $v->content ?></div>'
);
$this->createTemplate(
'child.php',
'<?php $v->extend("parent") ?>' . PHP_EOL .
'<p>Lorem ipsum</p>' . PHP_EOL
);
$result = $this->factory->render('child');
self::assertSame(
'<h2>Foo</h2>' . PHP_EOL .
'<div class="content"><p>Lorem ipsum</p></div>',
$result
);
}
/** @test */
public function defineDefaultsWithCoalesce()
{
$this->createTemplate(
'parent.php',
'<h2><?= $v->title ?? "Foo" ?></h2>' . PHP_EOL .
'<div class="content"><?= $v->content ?></div>'
);
$this->createTemplate(
'child.php',
'<?php $v->extend("parent") ?>' . PHP_EOL .
'<p>Lorem ipsum</p>' . PHP_EOL
);
$result = $this->factory->render('child');
self::assertSame(
'<h2>Foo</h2>' . PHP_EOL .
'<div class="content"><p>Lorem ipsum</p></div>',
$result
);
}
/** @test */
public function parentsGetAllDataByDefault()
{
......@@ -69,4 +146,40 @@ class ExtendingAndPartialsTest extends TestCase
self::assertSame('<h2><small>Foo Bar</small></h2>', $result);
}
/** @test */
public function fetchRendersAPartialTemplate()
{
$this->createTemplate('fetch.php', '<?= $v->fetch("partial", ["value" => "John Doe", "name" => "name"]) ?>');
$this->createTemplate('partial.php', '<input type="text" name="<?= $e($name) ?>" value="<?= $e($value) ?>">');
$result = $this->factory->render('fetch');
self::assertSame('<input type="text" name="name" value="John Doe">', $result);
}
/** @test */
public function theParentsParentGetsSectionsFromChild()
{
$this->createTemplate('grandParent.php', '<h1><?= $v->title ?></h1><?= $v->content ?>');
$this->createTemplate(
'parent.php',
'<?php $v->extend("grandParent") ?>' . PHP_EOL .
'<div class="content"><?= $v->content ?></div>'
);
$this->createTemplate(
'child.php',
'<?php $v->extend("parent") ?>' . PHP_EOL .
'<?php $v->provide("title", "Foo Bar") ?>' . PHP_EOL .
'<p>Lorem ipsum</p>'
);
$result = $this->factory->render('child');
self::assertSame(
'<h1>Foo Bar</h1>' .
'<div class="content"><p>Lorem ipsum</p></div>',
$result
);
}
}
......@@ -7,6 +7,13 @@ use Syna\Test\TestCase;
use Mockery as m;
use Syna\ViewLocator;
/**
* Class HandlingSectionsTest
*
* @package Syna\Test\View
* @author Thomas Flori <thflori@gmail.com>
* @covers \Syna\View
*/
class HandlingSectionsTest extends TestCase
{
/** @var m\Mock|Factory */
......
<?php
namespace Syna\Test\View;
use Syna\Factory;
use Syna\Test\TestCase;
use Mockery as m;
use Syna\ViewLocator;
/**
* Class LayoutsTest
*
* @package Syna\Test\View
* @author Thomas Flori <thflori@gmail.com>
* @covers \Syna\View
*/
class LayoutsTest extends TestCase
{
/** @var m\Mock|Factory */
protected $factory;
protected function setUp()
{
parent::setUp();
$this->factory = m::mock(Factory::class, [
new ViewLocator($this->templatePath . '/views'),
null,
new ViewLocator($this->templatePath . '/layouts')
])->makePartial();
}
/** @test */
public function itThrowsWhenNoLayoutLocatorIsDefined()
{
$factory = new Factory(new ViewLocator($this->templatePath));
$this->createTemplate('view.php', '<p>Lorem ipsum</p>');
self::expectException(\LogicException::class);
self::expectExceptionMessage('No locator for layout defined');
$factory->render('view', [], 'layout');
}
/** @test */
public function layoutGetsTheContentAsSection()
{
$this->createTemplate('views/view.php', '<p>Lorem ipsum</p>');
$this->createTemplate('layouts/layout.php', '<div class="content"><?= $v->section("content") ?></div>');
$result = $this->factory->render('view', [], 'layout');
self::assertSame('<div class="content"><p>Lorem ipsum</p></div>', $result);
}
/** @test */
public function layoutGetsAllSectionsDefined()
{
$this->createTemplate('views/view.php', '<?php $v->provide("title", "Foo") ?><p>Lorem ipsum</p>');
$this->createTemplate(
'layouts/layout.php',
'<h1><?= $v->section("title") ?></h1>' . PHP_EOL .
'<div class="content"><?= $v->section("content") ?></div>'
);
$result = $this->factory->render('view', [], 'layout');
self::assertSame(
'<h1>Foo</h1>' . PHP_EOL .
'<div class="content"><p>Lorem ipsum</p></div>',
$result
);
}
/** @test */
public function provideSharedDataForLayouts()
{
$this->createTemplate('views/view.php', '<p>Lorem ipsum</p>');
$this->createTemplate(
'layouts/layout.php',
'<h1><?= $title ?></h1>' . PHP_EOL .
'<div class="content"><?= $v->section("content") ?></div>'
);
$this->factory->addSharedData(['title' => 'Foo Bar']);
$result = $this->factory->render('view', [], 'layout');
self::assertSame(
'<h1>Foo Bar</h1>' . PHP_EOL .
'<div class="content"><p>Lorem ipsum</p></div>',
$result
);
}
}
......@@ -8,6 +8,13 @@ use Mockery as m;
use Syna\View;
use Syna\ViewLocator;
/**
* Class UsingHelpersTest
*
* @package Syna\Test\View
* @author Thomas Flori <thflori@gmail.com>
* @covers \Syna\View
*/
class UsingHelpersTest extends TestCase
{
/** @var m\Mock|Factory */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment