From 3613a04f493cfb29ed97ce0a79e4e905f09265fe Mon Sep 17 00:00:00 2001 From: Abel Hoogeveen Date: Mon, 11 Feb 2019 19:23:45 +0100 Subject: [PATCH] Implemented unit tests for Router. Fixes #4 --- .gitlab-ci.yml | 4 +- .../RouterLoadViewAndControllerEvent.php | 2 +- src/FuzeWorks/Router.php | 36 ++- test/mcr/RouterTest.php | 212 +++++++++++++++++- test/phpunit.xml | 2 +- 5 files changed, 231 insertions(+), 25 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 37621d3..14bb468 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -68,7 +68,9 @@ release: only: - master script: - - vendor/bin/phpunit -c test/phpunit.xml --coverage-text + - pecl install xdebug + - docker-php-ext-enable xdebug + - vendor/bin/phpunit -c test/phpunit.xml --coverage-text artifacts: name: "${CI_BUILD_NAME}_${CI_BUILD_REF_NAME}" paths: diff --git a/src/FuzeWorks/Event/RouterLoadViewAndControllerEvent.php b/src/FuzeWorks/Event/RouterLoadViewAndControllerEvent.php index 03cc4aa..d072c33 100644 --- a/src/FuzeWorks/Event/RouterLoadViewAndControllerEvent.php +++ b/src/FuzeWorks/Event/RouterLoadViewAndControllerEvent.php @@ -43,7 +43,7 @@ use FuzeWorks\Event; * * Use this to cancel the loading of a combination, or change the details of what is loaded. * - * Currently only used by OldRouter::defaultCallable(); + * Currently only used by Router::defaultCallable(); * * @author Abel Hoogeveen * @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net) diff --git a/src/FuzeWorks/Router.php b/src/FuzeWorks/Router.php index 1cf7e7a..e697ea3 100644 --- a/src/FuzeWorks/Router.php +++ b/src/FuzeWorks/Router.php @@ -36,7 +36,6 @@ namespace FuzeWorks; - use FuzeWorks\Event\RouterLoadViewAndControllerEvent; use FuzeWorks\Exception\ConfigException; use FuzeWorks\Exception\ControllerException; @@ -204,6 +203,7 @@ class Router $this->route = $route; // Call callable if routeConfig is callable, so routeConfig can be replaced + // This is an example of 'Dynamic Rewrite' // e.g: '.*$' => callable if (is_callable($routeConfig)) $routeConfig = call_user_func_array($routeConfig, [$matches]); @@ -212,17 +212,20 @@ class Router if (is_array($routeConfig)) { // Replace defaultCallable if a custom callable is provided + // This is an example of 'Custom Callable' // e.g: '.*$' => ['callable' => [$object, 'method']] if (isset($routeConfig['callable']) && is_callable($routeConfig['callable'])) $this->callable = $routeConfig['callable']; // If the route provides a configuration, use that + // This is an example of 'Static Rewrite' // e.g: '.*$' => ['viewName' => 'custom', 'viewType' => 'cli', 'function' => 'index'] else $this->matches = array_merge($this->matches, $routeConfig); } // If no custom callable is provided, use default + // This is an example of 'Default Callable' if (is_null($this->callable)) $this->callable = [$this, 'defaultCallable']; @@ -237,7 +240,7 @@ class Router return $output; } - throw new NotFoundException("Could not load view. Router could not find matching route."); + throw new NotFoundException("Could not load view. Router could not find matching route with satisfied callable."); } /** @@ -273,14 +276,10 @@ class Router Logger::log('defaultCallable called'); // Prepare variables - $viewName = isset($matches['viewName']) ? $matches['viewName'] : null; - $viewType = isset($matches['viewType']) ? $matches['viewType'] : $this->config->routing->default_viewType; - $viewMethod = isset($matches['viewMethod']) ? $matches['viewMethod'] : $this->config->routing->default_viewMethod; - $viewParameters = isset($matches['viewParameters']) ? $matches['viewParameters'] : ''; - - // If nothing is provided, cancel loading - if (is_null($viewName)) - return false; + $viewName = !empty($matches['viewName']) ? $matches['viewName'] : $this->config->routing->default_view; + $viewType = !empty($matches['viewType']) ? $matches['viewType'] : $this->config->routing->default_viewType; + $viewMethod = !empty($matches['viewMethod']) ? $matches['viewMethod'] : $this->config->routing->default_viewMethod; + $viewParameters = !empty($matches['viewParameters']) ? $matches['viewParameters'] : ''; try { /** @var RouterLoadViewAndControllerEvent $event */ @@ -305,6 +304,7 @@ class Router } catch (ControllerException $e) { throw new RouterException("Could not load view. Controllers::get threw ControllerException: '".$e->getMessage()."'"); } catch (NotFoundException $e) { + Logger::logError("Could not load view. Controller does not exist."); return false; } @@ -314,6 +314,7 @@ class Router } catch (ViewException $e) { throw new RouterException("Could not load view. Views::get threw ViewException: '".$e->getMessage()."'"); } catch (NotFoundException $e) { + Logger::logError("Could not load view. View does not exist."); return false; } @@ -324,20 +325,13 @@ class Router // Check if requested function or magic method exists in view if (method_exists($this->view, $event->viewMethod) || method_exists($this->view, '__call')) { - // Run viewCallMethodEvent. - try { - $methodEvent = Events::fireEvent('viewCallMethodEvent'); - } catch (EventException $e) { - throw new RouterException("Could not load view. viewCallMethodEvent threw exception: '".$e->getMessage()."'"); - } - - // If cancelled, halt - if ($methodEvent->isCancelled()) - throw new HaltException("Will not load view. Cancelled by viewCallMethodEvent"); - // Execute the function on the view return $this->view->{$event->viewMethod}($event->viewParameters); } + else + { + Logger::logError("Could not load view. View does not have method '".$event->viewMethod."'"); + } // View could not be found return false; diff --git a/test/mcr/RouterTest.php b/test/mcr/RouterTest.php index 464a23d..cb6641f 100644 --- a/test/mcr/RouterTest.php +++ b/test/mcr/RouterTest.php @@ -35,7 +35,9 @@ */ use FuzeWorks\Config; +use FuzeWorks\Events; use FuzeWorks\Factory; +use FuzeWorks\Priority; use FuzeWorks\Router; /** @@ -279,7 +281,7 @@ class RouterTest extends MVCRTestAbstract * @covers ::defaultCallable * @expectedException \FuzeWorks\Exception\HaltException */ - public function testDefaultCallableHalt() + public function testDefaultCallableHaltByView() { $matches = [ 'viewName' => 'TestDefaultCallableHalt', @@ -312,8 +314,216 @@ class RouterTest extends MVCRTestAbstract $this->assertNull($this->router->getCurrentView()); } + /** + * @depends testDefaultCallable + * @covers ::defaultCallable + * @covers \FuzeWorks\Event\RouterLoadViewAndControllerEvent::init + * @expectedException \FuzeWorks\Exception\HaltException + */ + public function testDefaultCallableHaltByEvent() + { + $matches = [ + 'viewName' => 'TestDefaultCallable', + 'viewType' => 'test', + 'viewMethod' => 'missing' + ]; + + $this->assertNull($this->router->getCurrentController()); + $this->assertNull($this->router->getCurrentView()); + + // Create listener + Events::addListener(function($event){ + $this->assertInstanceOf('\FuzeWorks\Event\RouterLoadViewAndControllerEvent', $event); + $this->assertEquals('TestDefaultCallable', $event->viewName); + $this->assertEquals('test', $event->viewType); + $this->assertEquals('missing', $event->viewMethod); + $event->setCancelled(true); + }, 'routerLoadViewAndControllerEvent'); + + $this->router->defaultCallable($matches, '.*$'); + } + + /** + * @depends testDefaultCallableHaltByEvent + * @covers ::defaultCallable + * @covers \FuzeWorks\Event\RouterLoadViewAndControllerEvent::overrideController + */ + public function testDefaultCallableReplaceController() + { + $matches = [ + 'viewName' => 'TestDefaultCallable', + 'viewType' => 'test', + 'viewMethod' => 'missing' + ]; + + $this->assertNull($this->router->getCurrentController()); + $this->assertNull($this->router->getCurrentView()); + + $mockController = $this->getMockBuilder('\FuzeWorks\Controller')->getMock(); + + // Create listener + Events::addListener(function($event, $mockController){ + $event->overrideController($mockController); + }, 'routerLoadViewAndControllerEvent', Priority::NORMAL, $mockController); + + $this->router->defaultCallable($matches, '.*$'); + $this->assertEquals($mockController, $this->router->getCurrentController()); + } + /* route() ------------------------------------------------------------ */ + /** + * @depends testDefaultCallable + * @covers ::route + * @covers ::loadCallable + */ + public function testRoute() + { + // Add route first + $this->router->addRoute('(?P.*?)(|\/(?P.*?)(|\/(?P.*?)))(|\.(?P.*?))'); + // Create mock view and controller + $mockController = $this->getMockBuilder('\FuzeWorks\Controller')->getMock(); + $mockView = $this->getMockBuilder('\FuzeWorks\View')->setMethods(['testMethod'])->getMock(); + class_alias(get_class($mockController), '\Application\Controller\TestRouteController'); + class_alias(get_class($mockView), '\Application\View\TestRouteTestView'); + + // Attempt to route + $this->assertnull($this->router->route('testRoute/testMethod/testParameters.test')); + $this->assertInstanceOf('\Application\Controller\TestRouteController', $this->router->getCurrentController()); + $this->assertInstanceOf('\Application\View\TestRouteTestView', $this->router->getCurrentView()); + } + + /** + * @depends testRoute + * @covers ::route + * @expectedException \FuzeWorks\Exception\NotFoundException + */ + public function testRouteNotFound() + { + $this->router->route('NotFound'); + } + + /** + * @depends testRouteNotFound + * @covers ::route + * @expectedException \FuzeWorks\Exception\NotFoundException + */ + public function testRouteNotMatched() + { + $this->router->addRoute('NotMatched'); + $this->router->route('NotFound'); + } + + /** + * @depends testRoute + * @covers ::route + * @covers ::loadCallable + */ + public function testRouteStaticRewrite() + { + // Add route first + $this->router->addRoute( + 'staticRewrite', + [ + 'viewName' => 'TestStaticRewrite', + 'viewMethod' => 'someMethod', + 'viewType' => 'static' + ] + ); + + // Create mock view and controller + $mockController = $this->getMockBuilder('\FuzeWorks\Controller')->getMock(); + $mockView = $this->getMockBuilder('\FuzeWorks\View')->setMethods(['someMethod'])->getMock(); + class_alias(get_class($mockController), '\Application\Controller\TestStaticRewriteController'); + class_alias(get_class($mockView), '\Application\View\TestStaticRewriteStaticView'); + + // Attempt to route + $this->assertnull($this->router->route('staticRewrite')); + $this->assertInstanceOf('\Application\Controller\TestStaticRewriteController', $this->router->getCurrentController()); + $this->assertInstanceOf('\Application\View\TestStaticRewriteStaticView', $this->router->getCurrentView()); + } + + /** + * @depends testRouteStaticRewrite + * @covers ::route + * @covers ::loadCallable + */ + public function testRouteDynamicRewrite() + { + // Add route first + $this->router->addRoute( + 'dynamicRewrite', + function($matches){ + $this->assertEquals([0=>'dynamicRewrite'], $matches); + return [ + 'viewName' => 'TestDynamicRewrite', + 'viewMethod' => 'someMethod', + 'viewType' => 'static' + ]; + } + ); + + // Create mock view and controller + $mockController = $this->getMockBuilder('\FuzeWorks\Controller')->getMock(); + $mockView = $this->getMockBuilder('\FuzeWorks\View')->setMethods(['someMethod'])->getMock(); + class_alias(get_class($mockController), '\Application\Controller\TestDynamicRewriteController'); + class_alias(get_class($mockView), '\Application\View\TestDynamicRewriteStaticView'); + + // Attempt to route + $this->assertNull($this->router->route('dynamicRewrite')); + $this->assertInstanceOf('\Application\Controller\TestDynamicRewriteController', $this->router->getCurrentController()); + $this->assertInstanceOf('\Application\View\TestDynamicRewriteStaticView', $this->router->getCurrentView()); + } + + /** + * @depends testRouteStaticRewrite + * @covers ::route + * @covers ::loadCallable + */ + public function testRouteCustomCallable() + { + // Create custom callable + $callable = function(array $matches, string $route){ + $this->assertEquals('customCallable', $route); + $this->assertEquals([0=>'customCallable'], $matches); + }; + + // Add route + $this->router->addRoute( + 'customCallable', + [ + 'callable' => $callable + ] + ); + + $this->assertNull($this->router->route('customCallable')); + } + + /** + * @depends testRouteStaticRewrite + * @covers ::route + * @covers ::loadCallable + * @expectedException \FuzeWorks\Exception\NotFoundException + */ + public function testRouteUnsatisfiedCallable() + { + // Create custom callable + $callable = function(array $matches, string $route){ + $this->assertEquals('unsatisfiedCallable', $route); + $this->assertEquals([0=>'unsatisfiedCallable'], $matches); + return false; + }; + + // Add route + $this->router->addRoute( + 'unsatisfiedCallable', + [ + 'callable' => $callable + ] + ); + + $this->assertNull($this->router->route('unsatisfiedCallable')); + } } diff --git a/test/phpunit.xml b/test/phpunit.xml index c758298..7708a45 100644 --- a/test/phpunit.xml +++ b/test/phpunit.xml @@ -27,7 +27,7 @@ ../ ../vendor/ - ../tests/ + ../test/