summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnold Daniels <arnold@jasny.net>2016-11-17 16:18:04 +0100
committerArnold Daniels <arnold@jasny.net>2016-11-17 16:18:04 +0100
commitcff30c5c3d529c23e40fa97df17308b8227a86c5 (patch)
treef141b8c05d75c5b86819edb63213ec8b93e281bd
parent14bc7de53fda2c44482c7cf46b896cfdd41aa68d (diff)
parent7149ef62ca11d451dadd271f5cb63233a4ac0748 (diff)
downloadcontroller-cff30c5c3d529c23e40fa97df17308b8227a86c5.zip
controller-cff30c5c3d529c23e40fa97df17308b8227a86c5.tar.gz
controller-cff30c5c3d529c23e40fa97df17308b8227a86c5.tar.bz2
Merge branch 'Minstel-Flash'
-rw-r--r--composer.json9
-rw-r--r--src/Controller.php22
-rw-r--r--src/Flash.php109
-rw-r--r--src/View/Twig.php178
-rw-r--r--tests/ControllerTest.php39
-rw-r--r--tests/FlashTest.php147
-rw-r--r--tests/View/TwigTest.php320
7 files changed, 821 insertions, 3 deletions
diff --git a/composer.json b/composer.json
index fab78e9..4a67d09 100644
--- a/composer.json
+++ b/composer.json
@@ -20,7 +20,13 @@
"jasny/php-functions": "^2.0"
},
"require-dev": {
- "jasny/php-code-quality": "^2.0"
+ "jasny/php-code-quality": "^2.0",
+ "jasny/twig-extensions": "^1.0",
+ "twig/twig": "^1.26"
+ },
+ "suggest": {
+ "jasny/twig-extensions": "Usefull extensions for Twig",
+ "twig/twig": "Needed to work with Twig templates"
},
"autoload": {
"psr-4": {
@@ -28,4 +34,3 @@
}
}
}
-
diff --git a/src/Controller.php b/src/Controller.php
index 057653e..9f69160 100644
--- a/src/Controller.php
+++ b/src/Controller.php
@@ -41,6 +41,12 @@ abstract class Controller
'application/x-www-form-urlencoded' => 'post',
'multipart/form-data' => 'post'
];
+
+ /**
+ * Flash
+ * @var Flash
+ */
+ protected $flash = null;
/**
* Run the controller
@@ -282,6 +288,21 @@ abstract class Controller
return $errorResponse;
}
+
+ /**
+ * Set the flash message and/or return the flash object.
+ *
+ * @param mixed $type flash type, eg. 'error', 'notice' or 'success'
+ * @param mixed $message flash message
+ * @return Flash
+ */
+ public function flash($type = null, $message = null)
+ {
+ if (!isset($this->flash)) $this->flash = new Flash();
+ if ($type && $message) $this->flash->set($type, $message);
+
+ return $this->flash;
+ }
/**
* Check if response is 2xx succesful, or empty
@@ -516,4 +537,3 @@ abstract class Controller
return $request ? $request->getMethod() : '';
}
}
-
diff --git a/src/Flash.php b/src/Flash.php
new file mode 100644
index 0000000..ad060f9
--- /dev/null
+++ b/src/Flash.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace Jasny;
+
+/**
+ * Class for the flash message
+ */
+class Flash
+{
+ /**
+ * @var object
+ */
+ protected static $data = null;
+
+ /**
+ * Check if the flash is set.
+ *
+ * @return boolean
+ */
+ public static function isIssued()
+ {
+ return isset($_SESSION['flash']) || isset(static::$data);
+ }
+
+ /**
+ * Set the flash.
+ *
+ * @param mixed $type flash type, eg. 'error', 'notice' or 'success'
+ * @param mixed $message flash message
+ */
+ public static function set($type, $message)
+ {
+ if (!$type) {
+ throw new \InvalidArgumentException("Type should not be empty");
+ }
+
+ static::$data = (object)['type'=>$type, 'message'=>$message];
+
+ $_SESSION['flash'] = static::$data;
+ }
+
+ /**
+ * Get the flash.
+ *
+ * @return object
+ */
+ public static function get()
+ {
+ if (!isset(static::$data) && isset($_SESSION['flash'])) {
+ static::$data = (object)$_SESSION['flash'];
+ unset($_SESSION['flash']);
+ }
+
+ return static::$data;
+ }
+
+ /**
+ * Reissue the flash.
+ */
+ public static function reissue()
+ {
+ if (!isset(static::$data) && isset($_SESSION['flash'])) {
+ static::$data = (object)$_SESSION['flash'];
+ } else {
+ $_SESSION['flash'] = static::$data;
+ }
+ }
+
+ /**
+ * Clear the flash.
+ */
+ public static function clear()
+ {
+ self::$data = null;
+ unset($_SESSION['flash']);
+ }
+
+ /**
+ * Get the flash type
+ *
+ * @return string
+ */
+ public static function getType()
+ {
+ $data = static::get();
+ return isset($data) ? $data->type : null;
+ }
+
+ /**
+ * Get the flash message
+ *
+ * @return string
+ */
+ public static function getMessage()
+ {
+ $data = static::get();
+ return isset($data) ? $data->message : null;
+ }
+
+ /**
+ * Cast object to string
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string)$this->getMessage();
+ }
+}
diff --git a/src/View/Twig.php b/src/View/Twig.php
new file mode 100644
index 0000000..ee5d4ce
--- /dev/null
+++ b/src/View/Twig.php
@@ -0,0 +1,178 @@
+<?php
+
+namespace Jasny\Controller\View;
+
+use Jasny\Flash;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * View using Twig
+ */
+trait Twig
+{
+ /**
+ * Twig environment
+ * @var \Twig_Environment
+ */
+ protected $twig = null;
+
+ /**
+ * Get server request
+ * @return ServerRequestInterface
+ */
+ abstract public function getRequest();
+
+ /**
+ * Get server response
+ * @return ResponseInterface
+ */
+ abstract public function getResponse();
+
+ /**
+ * Add a global variable to the view.
+ *
+ * @param string $name Variable name
+ * @param mixed $value
+ * @return $this
+ */
+ public function setViewVariable($name, $value)
+ {
+ if (!$name) throw new \InvalidArgumentException("Name should not be empty");
+
+ $this->getTwig()->addGlobal($name, $value);
+
+ return $this;
+ }
+
+ /**
+ * Expose a function to the view.
+ *
+ * @param string $name Variable name
+ * @param mixed $function
+ * @param string $as 'function' or 'filter'
+ * @return $this
+ */
+ public function setViewFunction($name, $function = null, $as = 'function')
+ {
+ if ($as === 'function') {
+ $this->getTwig()->addFunction($this->createTwigFunction($name, $function));
+ } elseif ($as === 'filter') {
+ $this->getTwig()->addFilter($this->createTwigFilter($name, $function));
+ } else {
+ throw new \InvalidArgumentException("You should create either function or filter, not '$as'");
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add extension to view
+ *
+ * @param object $extension
+ * @return $this
+ */
+ public function setViewExtension($extension)
+ {
+ $this->getTwig()->addExtension($extension);
+
+ return $this;
+ }
+
+ /**
+ * View rendered template
+ *
+ * @param string $name Template name
+ * @param array $context Template context
+ * @return ResponseInterface
+ */
+ public function view($name, array $context = [])
+ {
+ if (!pathinfo($name, PATHINFO_EXTENSION)) $name .= '.html.twig';
+
+ $twig = $this->getTwig();
+ $tmpl = $twig->loadTemplate($name);
+
+ $response = $this->getResponse();
+ $response = $response->withHeader('Content-Type', 'text/html; charset=' . $twig->getCharset());
+ $response->getBody()->write($tmpl->render($context));
+
+ return $response;
+ }
+
+ /**
+ * Get twig environment
+ *
+ * @return \Twig_Environment
+ */
+ public function getTwig()
+ {
+ if ($this->twig) return $this->twig;
+
+ $loader = $this->getTwigLoader();
+ $this->twig = $this->getTwigEnvironment($loader);
+
+ $extensions = ['DateExtension', 'PcreExtension', 'TextExtension', 'ArrayExtension'];
+ foreach ($extensions as $name) {
+ $class = "Jasny\Twig\\$name";
+
+ if (class_exists($class)) $this->setViewExtension(new $class());
+ }
+
+ $uri = $this->getRequest()->getUri()->getPath();
+
+ $this->setViewVariable('current_url', $uri);
+ $this->setViewVariable('flash', new Flash());
+
+ return $this->twig;
+ }
+
+ /**
+ * Get twig loasder for current working directory
+ *
+ * @return \Twig_Loader_Filesystem
+ */
+ public function getTwigLoader()
+ {
+ return new \Twig_Loader_Filesystem(getcwd());
+ }
+
+ /**
+ * Get twig environment instance
+ *
+ * @param \Twig_Loader_Filesystem $loader
+ * @return \Twig_Environment
+ */
+ public function getTwigEnvironment(\Twig_Loader_Filesystem $loader)
+ {
+ return new \Twig_Environment($loader);
+ }
+
+ /**
+ * Create twig function
+ *
+ * @param string $name Name of function in view
+ * @param callable|null $function
+ * @return \Twig_SimpleFunction
+ */
+ public function createTwigFunction($name, $function)
+ {
+ if (!$name) throw new \InvalidArgumentException("Function name should not be empty");
+
+ return new \Twig_SimpleFunction($name, $function ?: $name);
+ }
+
+ /**
+ * Create twig filter
+ *
+ * @param string $name Name of filter in view
+ * @param callable|null $function
+ * @return \Twig_SimpleFilter
+ */
+ public function createTwigFilter($name, $function)
+ {
+ if (!$name) throw new \InvalidArgumentException("Filter name should not be empty");
+
+ return new \Twig_SimpleFilter($name, $function ?: $name);
+ }
+}
diff --git a/tests/ControllerTest.php b/tests/ControllerTest.php
index e0ebdc2..3c659b2 100644
--- a/tests/ControllerTest.php
+++ b/tests/ControllerTest.php
@@ -1,6 +1,7 @@
<?php
use Jasny\Controller;
+use Jasny\Flash;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
@@ -81,6 +82,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
}
/**
+<<<<<<< HEAD
* Test functions that check request method
*
* @dataProvider requestMethodProvider
@@ -313,6 +315,43 @@ class ControllerTest extends PHPUnit_Framework_TestCase
['forbidden', 409, false],
['badRequest', 400, true],
['badRequest', 403, false]
+ ];
+ }
+
+ /**
+ * Test setting flash
+ *
+ * @dataProvider flashProvider
+ * @param object $data
+ */
+ public function testFlash($data)
+ {
+ $controller = $this->getMockBuilder(Controller::class)->disableOriginalConstructor()->getMockForAbstractClass();
+
+ $flash = $controller->flash();
+ $this->assertInstanceOf(Flash::class, $flash, "Flash is not set");
+ $this->assertEmpty($flash->get(), "Flash data should be empty");
+
+ $flash = $controller->flash($data->type, $data->message);
+ $this->assertInstanceOf(Flash::class, $flash, "Flash is not set");
+ $this->assertEquals($data, $flash->get(), "Flash data is incorrect");
+
+ $flash = $controller->flash();
+ $this->assertInstanceOf(Flash::class, $flash, "Flash is not set");
+ $this->assertEquals($data, $flash->get(), "Flash data is incorrect");
+
+ $flash->clear();
+ }
+
+ /**
+ * Test setting flash
+ *
+ * @return array
+ */
+ public function flashProvider()
+ {
+ return [
+ [(object)['type' => 'test_type', 'message' => 'Test message']]
];
}
diff --git a/tests/FlashTest.php b/tests/FlashTest.php
new file mode 100644
index 0000000..c17c446
--- /dev/null
+++ b/tests/FlashTest.php
@@ -0,0 +1,147 @@
+<?php
+
+use Jasny\Flash;
+
+/**
+ * @covers Jasny\Flash
+ */
+class FlashTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * Test flash
+ *
+ * @dataProvider flashProvider
+ * @param object $data
+ */
+ public function testFlash($data)
+ {
+ $flash = new Flash();
+ $this->assertFlashEmpty($flash);
+
+ //Set flash
+ $flash->set($data->type, $data->message);
+ $this->assertFlashDataCorrect($flash, $data);
+ $this->assertFlashIsIssued($flash, $data);
+
+ //Get data
+ $setData = $flash->get();
+ $this->assertFlashDataCorrect($flash, $data);
+ $this->assertFlashIsIssued($flash, $data);
+ $this->assertEquals($data, $setData, "Flash data was not got correctly");
+
+ //Clear
+ $flash->clear();
+ $this->assertFlashEmpty($flash);
+
+ //Set from session
+ $_SESSION['flash'] = $data;
+ $this->assertFlashIsIssued($flash, $data);
+
+ //When flash is set only in session, not by 'set' method, then getting it's data removes flash from session, so they only remain in flash object
+ $this->assertFlashDataCorrect($flash, $data);
+ $this->assertFalse(isset($_SESSION['flash']), "Session flash variable should be empty");
+ $this->assertTrue($flash->isIssued(), "Flash should be issued");
+
+ //Clear
+ $flash->clear();
+ $this->assertFlashEmpty($flash);
+
+ //Reissue from session
+ $_SESSION['flash'] = $data;
+ $flash->reissue();
+
+ $this->assertFlashDataCorrect($flash, $data);
+ $this->assertFlashIsIssued($flash, $data);
+
+ //Reissue from object data
+ unset($_SESSION['flash']);
+ $flash->reissue();
+
+ $this->assertFlashDataCorrect($flash, $data);
+ $this->assertFlashIsIssued($flash, $data);
+
+ //Clear
+ $flash->clear();
+ $this->assertFlashEmpty($flash);
+ }
+
+ /**
+ * Provide data for testing flash
+ *
+ * @return array
+ */
+ public function flashProvider()
+ {
+ return [
+ [(object)['type' => 'test_type', 'message' => 'Test message']],
+ [(object)['type' => 'test_type', 'message' => '']]
+ ];
+ }
+
+ /**
+ * Test 'set' method with wrong params
+ *
+ * @dataProvider setNegativeProvider
+ * @param object $data
+ */
+ public function testSetNegative($data)
+ {
+ $flash = new Flash();
+
+ $this->expectException(InvalidArgumentException::class);
+
+ $flash->set($data->type, $data->message);
+ }
+
+ /**
+ * Provide data for testing wrong set
+ *
+ * @return array
+ */
+ public function setNegativeProvider()
+ {
+ return [
+ [(object)['type' => '', 'message' => 'Test message']]
+ ];
+ }
+
+ /**
+ * Assert that flash is not set
+ *
+ * @param Flash $flash
+ */
+ public function assertFlashEmpty($flash)
+ {
+ $this->assertFalse(isset($_SESSION['flash']), "Session flash variable should be empty");
+ $this->assertFalse($flash->isIssued(), "Flash should not be issued");
+ $this->assertEmpty($flash->get(), "Flash should be empty");
+ $this->assertEmpty($flash->getType(), "Flash type should not be set");
+ $this->assertEmpty($flash->getMessage(), "Message should be empty");
+ $this->assertEmpty((string)$flash, "Flash should be empty");
+ }
+
+ /**
+ * Assert that flash is issued
+ *
+ * @param Flash $flash
+ * @param object $data
+ */
+ public function assertFlashIsIssued($flash, $data)
+ {
+ $this->assertEquals($data, $_SESSION['flash'], "Flash data was not set correctly");
+ $this->assertTrue($flash->isIssued(), "Flash should be issued");
+ }
+
+ /**
+ * Assert that flash data is correct
+ *
+ * @param Flash $flash
+ * @param object $data
+ */
+ public function assertFlashDataCorrect($flash, $data)
+ {
+ $this->assertEquals($data->type, $flash->getType(), "Type was not got correctly");
+ $this->assertEquals($data->message, $flash->getMessage(), "Message was not got correctly");
+ $this->assertEquals($data->message, (string)$flash, "Message was not got correctly when presenting flash as string");
+ }
+}
diff --git a/tests/View/TwigTest.php b/tests/View/TwigTest.php
new file mode 100644
index 0000000..662681d
--- /dev/null
+++ b/tests/View/TwigTest.php
@@ -0,0 +1,320 @@
+<?php
+
+use Jasny\Flash;
+use Jasny\Controller\View\Twig;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * @covers Jasny\Controller\View\Twig
+ */
+class TwigTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * Test creating twig environment
+ */
+ public function testGetTwig()
+ {
+ $view = $this->getView(['getTwigLoader', 'getTwigEnvironment', 'createTwigFunction', 'createTwigFilter', 'setViewExtension', 'setViewVariable']);
+ list($request, $response) = $this->getRequests();
+ list($loader, $env) = $this->getTwigObjects();
+
+ $view->expects($this->once())->method('getTwigLoader')->will($this->returnValue($loader));
+ $view->expects($this->once())->method('getTwigEnvironment')->with($this->equalTo($loader))->will($this->returnValue($env));
+ $view->expects($this->exactly(4))->method('setViewExtension')->with($this->callback(function($ext) {
+ return in_array(get_class($ext), ['Jasny\Twig\DateExtension', 'Jasny\Twig\PcreExtension', 'Jasny\Twig\TextExtension', 'Jasny\Twig\ArrayExtension'], true);
+ }))->will($this->returnSelf());
+
+ $path = '/test/request/path';
+ $uri = $this->createMock(UriInterface::class);
+
+ $view->expects($this->once())->method('getRequest')->will($this->returnValue($request));
+ $request->expects($this->once())->method('getUri')->will($this->returnValue($uri));
+ $uri->expects($this->once())->method('getPath')->will($this->returnValue($path));
+ $view->expects($this->exactly(2))->method('setViewVariable')->withConsecutive(
+ [$this->equalTo('current_url'), $this->equalTo($path)],
+ [$this->equalTo('flash'), $this->callback(function($flash) {
+ return $flash instanceof Flash && empty($flash->get());
+ })]
+ );
+
+ $result = $view->getTwig();
+ $resultSaved = $view->getTwig();
+
+ $this->assertEquals($env, $result, "Twig environment should be returned");
+ $this->assertEquals($env, $resultSaved, "Saved twig environment should be returned");
+ }
+
+ /**
+ * Test getting twig loader and environment
+ */
+ public function testGetTwigEnvironment()
+ {
+ $view = $this->getView();
+ $loader = $view->getTwigLoader();
+ $env = $view->getTwigEnvironment($loader);
+
+ $this->assertInstanceOf(Twig_Environment::class, $env);
+ $this->assertInstanceOf(Twig_Loader_Filesystem::class, $loader);
+ $this->assertEquals([getcwd()], $loader->getPaths(), "Twig loader should be configured for current dir");
+ }
+
+ /**
+ * Test 'view' function
+ *
+ * @dataProvider viewProvider
+ * @param string $tmplName Name of template to render
+ * @param string $tmplNameFull Name of template with extension
+ * @param array $context Variables to render
+ * @param string $charset Twig sharset
+ * @param string $html Rendered template
+ */
+ public function testView($tmplName, $tmplNameFull, $context, $charset, $html)
+ {
+ $view = $this->getView(['getTwig']);
+ list($request, $response) = $this->getRequests();
+ list($loader, $env, $tmpl) = $this->getTwigObjects();
+
+ $stream = $this->createMock(StreamInterface::class);
+
+ $view->expects($this->once())->method('getTwig')->will($this->returnValue($env));
+ $view->expects($this->once())->method('getResponse')->will($this->returnValue($response));
+ $env->expects($this->once())->method('loadTemplate')->with($this->equalTo($tmplNameFull))->will($this->returnValue($tmpl));
+ $env->expects($this->once())->method('getCharset')->will($this->returnValue($charset));
+ $response->expects($this->once())->method('withHeader')->with($this->equalTo('Content-Type'), $this->equalTo('text/html; charset=' . $charset ))->will($this->returnSelf());
+ $response->expects($this->once())->method('getBody')->will($this->returnValue($stream));
+ $tmpl->expects($this->once())->method('render')->with($this->equalTo($context))->will($this->returnValue($html));
+ $stream->expects($this->once())->method('write')->with($this->equalTo($html));
+
+ $result = $view->view($tmplName, $context);
+
+ $this->assertEquals($response, $result, "Response should be returned");
+ }
+
+ /**
+ * Provide data for testing view method
+ *
+ * @return array
+ */
+ public function viewProvider()
+ {
+ return [
+ ['test-template', 'test-template.html.twig', ['test' => 'value'], 'test-charset', 'rendered template'],
+ ['test-template.html.twig', 'test-template.html.twig', ['test' => 'value'], 'test-charset', 'rendered template'],
+ ];
+ }
+
+ /**
+ * Test creating twig function
+ *
+ * @dataProvider createTwigFunctionFilterProvider
+ * @param string $class Twig function or filter class
+ * @param string $createMethod
+ * @param string $name Created function or filter name
+ * @param callable $function
+ */
+ public function testCreateTwigFunctionFilter($class, $createMethod, $name, $function)
+ {
+ if (!$name) $this->expectException(InvalidArgumentException::class);
+
+ $view = $this->getView();
+ $result = $view->createTwigFunction($name, $function);
+ $callback = $result->getCallable();
+
+ $this->assertInstanceOf(Twig_SimpleFunction::class, $result, "Result must be an instance of 'Twig_SimpleFunction'");
+ $this->assertEquals($name, $result->getName(), "Function name is not set correctly");
+
+ if (!$function) {
+ $this->assertEquals($name, $callback);
+ } else {
+ $this->assertEquals(call_user_func($function), call_user_func($callback), "Function body was not set correctly");
+ }
+ }
+
+ /**
+ * Provide data for testing creating functions and filter for twig
+ *
+ * @return array
+ */
+ public function createTwigFunctionFilterProvider()
+ {
+ return [
+ [Twig_SimpleFunction::class, 'createTwigFunction', 'test_name', function() {return 'success_call';}],
+ [Twig_SimpleFunction::class, 'createTwigFunction', 'test_name', null],
+ [Twig_SimpleFilter::class, 'createTwigFilter', 'test_name', function() {return 'success_call';}],
+ [Twig_SimpleFilter::class, 'createTwigFilter', 'test_name', null],
+ [Twig_SimpleFunction::class, 'createTwigFunction', '', function() {return 'success_call';}],
+ [Twig_SimpleFilter::class, 'createTwigFilter', '', function() {return 'success_call';}],
+ ];
+ }
+
+ /**
+ * Test 'setViewFunction' method
+ *
+ * @dataProvider setViewFunctionFunctionProvider
+ * @param string $name
+ * @param callable $callable
+ * @param string $type
+ * @param boolean $positive
+ */
+ public function testSetViewFunctionFunction($name, $callable, $type, $positive)
+ {
+ $view = $this->getView(['getTwig', 'createTwigFunction', 'createTwigFilter']);
+ $twig = $this->createMock(Twig_Environment::class);
+ $function = $this->createMock(Twig_SimpleFunction::class);
+
+ if ($positive) {
+ $view->expects($this->once())->method('getTwig')->will($this->returnValue($twig));
+ $view->expects($this->once())->method('createTwigFunction')->with($this->equalTo($name), $this->equalTo($callable))->will($this->returnValue($function));
+ $view->expects($this->never())->method('createTwigFilter');
+ $twig->expects($this->once())->method('addFunction')->with($this->equalTo($function));
+ } else {
+ $this->expectException(InvalidArgumentException::class);
+ }
+
+ $result = $view->setViewFunction($name, $callable, $type);
+
+ $this->assertEquals($view, $result, "Method should return \$this");
+ }
+
+ /**
+ * Provide data for testing 'setViewFunction' method when creating functions
+ *
+ * @return array
+ */
+ public function setViewFunctionFunctionProvider()
+ {
+ return [
+ ['test_name', function() {}, 'function', true],
+ ['test_name', function() {}, 'not_function_or_filter', false]
+ ];
+ }
+
+ /**
+ * Test 'setViewFunction' method
+ *
+ * @dataProvider setViewFunctionFilterProvider
+ * @param string $name
+ * @param callable $callable
+ */
+ public function testSetViewFunctionFilter($name, $callable)
+ {
+ $view = $this->getView(['getTwig', 'createTwigFunction', 'createTwigFilter']);
+ $twig = $this->createMock(Twig_Environment::class);
+ $function = $this->createMock(Twig_SimpleFilter::class);
+
+ $view->expects($this->once())->method('getTwig')->will($this->returnValue($twig));
+ $view->expects($this->once())->method('createTwigFilter')->with($this->equalTo($name), $this->equalTo($callable))->will($this->returnValue($function));
+ $view->expects($this->never())->method('createTwigFunction');
+ $twig->expects($this->once())->method('addFilter')->with($this->equalTo($function));
+
+ $result = $view->setViewFunction($name, $callable, 'filter');
+
+ $this->assertEquals($view, $result, "Method should return \$this");
+ }
+
+ /**
+ * Provide data for testing 'setViewFunction' method when creating filter
+ *
+ * @return array
+ */
+ public function setViewFunctionFilterProvider()
+ {
+ return [
+ ['test_name', function() {}]
+ ];
+ }
+
+ /**
+ * Test 'setViewVariable' method
+ *
+ * @dataProvider setViewVariableProvider
+ */
+ public function testSetViewVariable($name, $value)
+ {
+ $view = $this->getView(['getTwig']);
+ $twig = $this->createMock(Twig_Environment::class);
+
+ if (!$name) {
+ $this->expectException(InvalidArgumentException::class);
+ } else {
+ $view->expects($this->once())->method('getTwig')->will($this->returnValue($twig));
+ $twig->expects($this->once())->method('addGlobal')->with($this->equalTo($name), $this->equalTo($value));
+ }
+
+ $result = $view->setViewVariable($name, $value);
+
+ $this->assertEquals($view, $result, "Method should return \$this");
+ }
+
+ /**
+ * Provide data for testing 'setViewVariable' method
+ *
+ * @return array
+ */
+ public function setViewVariableProvider()
+ {
+ return [
+ ['test_name', 'test_value'],
+ ['test_name', ''],
+ ['', 'test_value'],
+ ];
+ }
+
+ /**
+ * Test edding extension to twig
+ */
+ public function testSetViewExtension()
+ {
+ $view = $this->getView(['getTwig']);
+ $twig = $this->createMock(Twig_Environment::class);
+ $ext = $this->createMock(Twig_ExtensionInterface::class);
+
+ $view->expects($this->once())->method('getTwig')->will($this->returnValue($twig));
+ $twig->expects($this->once())->method('addExtension')->with($this->equalTo($ext));
+
+ $result = $view->setViewExtension($ext);
+
+ $this->assertEquals($view, $result, "Method should return \$this");
+ }
+
+ /**
+ * Get mock for testing trait
+ *
+ * @param array $methods Methods to mock
+ * @return Twig
+ */
+ public function getView($methods = [])
+ {
+ return $this->getMockForTrait(Twig::class, [], '', true, true, true, $methods);
+ }
+
+ /**
+ * Get mocks representing twig objects
+ *
+ * @return array
+ */
+ public function getTwigObjects()
+ {
+ return [
+ $this->createMock(Twig_Loader_Filesystem::class),
+ $this->createMock(Twig_Environment::class),
+ $this->createMock(Twig_Template::class)
+ ];
+ }
+
+ /**
+ * Get request and response instances
+ *
+ * @return array
+ */
+ public function getRequests()
+ {
+ return [
+ $this->createMock(ServerRequestInterface::class),
+ $this->createMock(ResponseInterface::class)
+ ];
+ }
+}