Skip to content
Projects
Groups
Snippets
Help
Sign in / Register
Toggle navigation
Minds Backend - Engine
Project overview
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Locked Files
Issues
293
Merge Requests
38
CI / CD
Security & Compliance
Packages
Wiki
Snippets
Members
Collapse sidebar
Close sidebar
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Minds
Minds Backend - Engine
Commits
c5c09ac1
Commit
c5c09ac1
authored
13 minutes ago
by
Emiliano Balbuena
Browse files
Options
Download
(wip)(test): Router spec tests
parent
703cdbb6
epic/modern-router
1 merge request
!342
WIP: (feat): Modernize Router (&75)
Pipeline
#101056855
passed with stages
in 6 minutes and 58 seconds
Changes
13
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
539 additions
and
22 deletions
+539
-22
Core/Router.php
View file @
c5c09ac1
...
...
@@ -70,7 +70,7 @@ class Router
->
withUri
(
(
new
Uri
(
$uri
))
->
withHost
(
$host
)
);
// TODO: Ensure it works with reverse proxy
);
$response
=
$this
->
dispatcher
->
pipe
(
new
Kernel\ContentNegotiationMiddleware
())
...
...
This diff is collapsed.
Core/Router/Middleware/AdminMiddleware.php
View file @
c5c09ac1
...
...
@@ -20,6 +20,15 @@ class AdminMiddleware implements MiddlewareInterface
/** @var string */
protected
$attributeName
=
'_user'
;
/** @var callable */
private
$xsrfValidateRequest
;
public
function
__construct
(
$xsrfValidateRequest
=
null
)
{
$this
->
xsrfValidateRequest
=
$xsrfValidateRequest
?:
[
XSRF
::
class
,
'validateRequest'
];
}
/**
* @param string $attributeName
* @return AdminMiddleware
...
...
@@ -46,7 +55,7 @@ class AdminMiddleware implements MiddlewareInterface
{
if
(
!
$request
->
getAttribute
(
$this
->
attributeName
)
||
!
XSRF
::
validateRequest
(
)
!
call_user_func
(
$this
->
xsrfValidateRequest
)
)
{
throw
new
UnauthorizedException
();
}
...
...
This diff is collapsed.
Core/Router/Middleware/LoggedInMiddleware.php
View file @
c5c09ac1
...
...
@@ -18,6 +18,15 @@ class LoggedInMiddleware implements MiddlewareInterface
/** @var string */
protected
$attributeName
=
'_user'
;
/** @var callable */
private
$xsrfValidateRequest
;
public
function
__construct
(
$xsrfValidateRequest
=
null
)
{
$this
->
xsrfValidateRequest
=
$xsrfValidateRequest
?:
[
XSRF
::
class
,
'validateRequest'
];
}
/**
* @param string $attributeName
* @return LoggedInMiddleware
...
...
@@ -43,7 +52,7 @@ class LoggedInMiddleware implements MiddlewareInterface
{
if
(
!
$request
->
getAttribute
(
$this
->
attributeName
)
||
!
XSRF
::
validateRequest
(
)
!
call_user_func
(
$this
->
xsrfValidateRequest
)
)
{
throw
new
UnauthorizedException
();
}
...
...
This diff is collapsed.
Core/Router/PrePsr7/Fallback.php
View file @
c5c09ac1
...
...
@@ -17,6 +17,25 @@ use Zend\Diactoros\Stream;
class
Fallback
{
/** @var string[] */
const
ALLOWED
=
[
'/api/v1/'
,
'/api/v2/'
,
'/emails/'
,
'/fs/v1'
,
'/oauth2/'
,
'/checkout'
,
'/deeplinks'
,
'/icon'
,
'/sitemap'
,
'/sitemaps'
,
'/thumbProxy'
,
'/archive'
,
'/wall'
,
'/not-supported'
,
'/apple-app-site-association'
,
];
/** @var Config */
protected
$config
;
...
...
@@ -36,28 +55,10 @@ class Fallback
*/
public
function
shouldRoute
(
string
$route
)
:
bool
{
$allowed
=
[
'/api/v1/'
,
'/api/v2/'
,
'/emails/'
,
'/fs/v1'
,
'/oauth2/'
,
'/checkout'
,
'/deeplinks'
,
'/icon'
,
'/sitemap'
,
'/sitemaps'
,
'/thumbProxy'
,
'/archive'
,
'/wall'
,
'/not-supported'
,
'/apple-app-site-association'
,
];
$route
=
sprintf
(
"/%s"
,
ltrim
(
$route
,
'/'
));
$shouldFallback
=
false
;
foreach
(
$allowed
as
$allowedRoute
)
{
foreach
(
static
::
ALLOWED
as
$allowedRoute
)
{
if
(
stripos
(
$route
,
$allowedRoute
)
===
0
)
{
$shouldFallback
=
true
;
break
;
...
...
This diff is collapsed.
Core/Router/Route.php
View file @
c5c09ac1
...
...
@@ -91,6 +91,7 @@ class Route
}
$route
=
sprintf
(
"/%s/%s"
,
trim
(
$this
->
getPrefix
(),
'/'
),
trim
(
$route
,
'/'
));
$route
=
trim
(
$route
,
'/'
);
foreach
(
$methods
as
$method
)
{
$this
->
registry
->
register
(
$method
,
$route
,
$binding
,
$this
->
middleware
);
...
...
This diff is collapsed.
Spec/Core/Router/Middleware/AdminMiddlewareSpec.php
View file @
c5c09ac1
...
...
@@ -2,14 +2,116 @@
namespace
Spec\Minds\Core\Router\Middleware
;
use
Minds\Core\Router\Exceptions\ForbiddenException
;
use
Minds\Core\Router\Exceptions\UnauthorizedException
;
use
Minds\Core\Router\Middleware\AdminMiddleware
;
use
Minds\Entities\User
;
use
PhpSpec\ObjectBehavior
;
use
Prophecy\Argument
;
use
Psr\Http\Message\ResponseInterface
;
use
Psr\Http\Message\ServerRequestInterface
;
use
Psr\Http\Server\RequestHandlerInterface
;
class
AdminMiddlewareSpec
extends
ObjectBehavior
{
public
function
let
()
{
$xsrfValidateRequest
=
function
()
{
/** XSRF::validateRequest() */
return
true
;
};
$this
->
beConstructedWith
(
$xsrfValidateRequest
);
}
public
function
it_is_initializable
()
{
$this
->
shouldHaveType
(
AdminMiddleware
::
class
);
}
public
function
it_should_process
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
,
ResponseInterface
$response
,
User
$user
)
{
$request
->
getAttribute
(
'_phpspec_user'
)
->
shouldBeCalled
()
->
willReturn
(
$user
);
$user
->
isAdmin
()
->
shouldBeCalled
()
->
willReturn
(
true
);
$handler
->
handle
(
$request
)
->
shouldBeCalled
()
->
willReturn
(
$response
);
$this
->
setAttributeName
(
'_phpspec_user'
)
->
process
(
$request
,
$handler
)
->
shouldReturn
(
$response
);
}
public
function
it_should_throw_unauthorized_if_no_user_during_process
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
)
{
$request
->
getAttribute
(
'_phpspec_user'
)
->
shouldBeCalled
()
->
willReturn
(
null
);
$handler
->
handle
(
$request
)
->
shouldNotBeCalled
();
$this
->
setAttributeName
(
'_phpspec_user'
)
->
shouldThrow
(
UnauthorizedException
::
class
)
->
duringProcess
(
$request
,
$handler
);
}
public
function
it_should_throw_unauthorized_if_xsrf_check_fail_during_process
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
,
User
$user
)
{
$this
->
beConstructedWith
(
function
()
{
/** XSRF::validateRequest() */
return
false
;
});
$request
->
getAttribute
(
'_phpspec_user'
)
->
shouldBeCalled
()
->
willReturn
(
$user
);
$handler
->
handle
(
$request
)
->
shouldNotBeCalled
();
$this
->
setAttributeName
(
'_phpspec_user'
)
->
shouldThrow
(
UnauthorizedException
::
class
)
->
duringProcess
(
$request
,
$handler
);
}
public
function
it_should_throw_forbidden_if_not_an_admin_during_process
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
,
User
$user
)
{
$request
->
getAttribute
(
'_phpspec_user'
)
->
shouldBeCalled
()
->
willReturn
(
$user
);
$user
->
isAdmin
()
->
shouldBeCalled
()
->
willReturn
(
false
);
$handler
->
handle
(
$request
)
->
shouldNotBeCalled
();
$this
->
setAttributeName
(
'_phpspec_user'
)
->
shouldThrow
(
ForbiddenException
::
class
)
->
duringProcess
(
$request
,
$handler
);
}
}
This diff is collapsed.
Spec/Core/Router/Middleware/Kernel/ContentNegotiationMiddlewareSpec.php
View file @
c5c09ac1
...
...
@@ -5,6 +5,9 @@ namespace Spec\Minds\Core\Router\Middleware\Kernel;
use
Minds\Core\Router\Middleware\Kernel\ContentNegotiationMiddleware
;
use
PhpSpec\ObjectBehavior
;
use
Prophecy\Argument
;
use
Psr\Http\Message\ResponseInterface
;
use
Psr\Http\Message\ServerRequestInterface
;
use
Psr\Http\Server\RequestHandlerInterface
;
class
ContentNegotiationMiddlewareSpec
extends
ObjectBehavior
{
...
...
@@ -12,4 +15,72 @@ class ContentNegotiationMiddlewareSpec extends ObjectBehavior
{
$this
->
shouldHaveType
(
ContentNegotiationMiddleware
::
class
);
}
public
function
it_should_process_and_mark_as_json
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
,
ResponseInterface
$response
)
{
$request
->
getHeader
(
'Accept'
)
->
shouldBeCalled
()
->
willReturn
([
'text/json; utf=8,application/json'
]);
$request
->
withAttribute
(
'accept'
,
'json'
)
->
shouldBeCalled
()
->
willReturn
(
$request
);
$handler
->
handle
(
$request
)
->
shouldBeCalled
()
->
willReturn
(
$response
);
$this
->
process
(
$request
,
$handler
)
->
shouldReturn
(
$response
);
}
public
function
it_should_process_and_mark_as_html
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
,
ResponseInterface
$response
)
{
$request
->
getHeader
(
'Accept'
)
->
shouldBeCalled
()
->
willReturn
([
'text/html; utf=8'
,
'application/xhtml+xml'
]);
$request
->
withAttribute
(
'accept'
,
'html'
)
->
shouldBeCalled
()
->
willReturn
(
$request
);
$handler
->
handle
(
$request
)
->
shouldBeCalled
()
->
willReturn
(
$response
);
$this
->
process
(
$request
,
$handler
)
->
shouldReturn
(
$response
);
}
public
function
it_should_process_and_leave_unmarked
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
,
ResponseInterface
$response
)
{
$request
->
getHeader
(
'Accept'
)
->
shouldBeCalled
()
->
willReturn
([
'image/png'
]);
$request
->
withAttribute
(
'accept'
,
Argument
::
cetera
())
->
shouldNotBeCalled
();
$handler
->
handle
(
$request
)
->
shouldBeCalled
()
->
willReturn
(
$response
);
$this
->
process
(
$request
,
$handler
)
->
shouldReturn
(
$response
);
}
}
This diff is collapsed.
Spec/Core/Router/Middleware/Kernel/CorsMiddlewareSpec.php
View file @
c5c09ac1
...
...
@@ -5,6 +5,10 @@ namespace Spec\Minds\Core\Router\Middleware\Kernel;
use
Minds\Core\Router\Middleware\Kernel\CorsMiddleware
;
use
PhpSpec\ObjectBehavior
;
use
Prophecy\Argument
;
use
Psr\Http\Message\ResponseInterface
;
use
Psr\Http\Message\ServerRequestInterface
;
use
Psr\Http\Server\RequestHandlerInterface
;
use
Zend\Diactoros\Response\EmptyResponse
;
class
CorsMiddlewareSpec
extends
ObjectBehavior
{
...
...
@@ -12,4 +16,39 @@ class CorsMiddlewareSpec extends ObjectBehavior
{
$this
->
shouldHaveType
(
CorsMiddleware
::
class
);
}
public
function
it_should_process_and_return_empty_response_if_options
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
)
{
$request
->
getMethod
()
->
shouldBeCalled
()
->
willReturn
(
'OPTIONS'
);
$request
->
getHeaderLine
(
'Origin'
)
->
shouldBeCalled
()
->
willReturn
(
'https://phpspec.test'
);
$this
->
process
(
$request
,
$handler
)
->
shouldBeAnInstanceOf
(
EmptyResponse
::
class
);
}
public
function
it_should_process_and_passthru_if_not_options
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
,
ResponseInterface
$response
)
{
$request
->
getMethod
()
->
shouldBeCalled
()
->
willReturn
(
'GET'
);
$handler
->
handle
(
$request
)
->
shouldBeCalled
()
->
willReturn
(
$response
);
$this
->
process
(
$request
,
$handler
)
->
shouldReturn
(
$response
);
}
}
This diff is collapsed.
Spec/Core/Router/Middleware/Kernel/EmptyResponseMiddlewareSpec.php
View file @
c5c09ac1
...
...
@@ -5,6 +5,10 @@ namespace Spec\Minds\Core\Router\Middleware\Kernel;
use
Minds\Core\Router\Middleware\Kernel\EmptyResponseMiddleware
;
use
PhpSpec\ObjectBehavior
;
use
Prophecy\Argument
;
use
Psr\Http\Message\ServerRequestInterface
;
use
Psr\Http\Server\RequestHandlerInterface
;
use
Zend\Diactoros\Response\HtmlResponse
;
use
Zend\Diactoros\Response\JsonResponse
;
class
EmptyResponseMiddlewareSpec
extends
ObjectBehavior
{
...
...
@@ -12,4 +16,30 @@ class EmptyResponseMiddlewareSpec extends ObjectBehavior
{
$this
->
shouldHaveType
(
EmptyResponseMiddleware
::
class
);
}
public
function
it_should_process_and_return_an_html_response
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
)
{
$request
->
getAttribute
(
'accept'
)
->
shouldBeCalled
()
->
willReturn
(
'html'
);
$this
->
process
(
$request
,
$handler
)
->
shouldBeAnInstanceOf
(
HtmlResponse
::
class
);
}
public
function
it_should_process_and_return_a_json_response
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
)
{
$request
->
getAttribute
(
'accept'
)
->
shouldBeCalled
()
->
willReturn
(
'json'
);
$this
->
process
(
$request
,
$handler
)
->
shouldBeAnInstanceOf
(
JsonResponse
::
class
);
}
}
This diff is collapsed.
Spec/Core/Router/Middleware/LoggedInMiddlewareSpec.php
View file @
c5c09ac1
...
...
@@ -2,14 +2,90 @@
namespace
Spec\Minds\Core\Router\Middleware
;
use
Minds\Core\Router\Exceptions\UnauthorizedException
;
use
Minds\Core\Router\Middleware\LoggedInMiddleware
;
use
Minds\Entities\User
;
use
PhpSpec\ObjectBehavior
;
use
Prophecy\Argument
;
use
Psr\Http\Message\ResponseInterface
;
use
Psr\Http\Message\ServerRequestInterface
;
use
Psr\Http\Server\RequestHandlerInterface
;
class
LoggedInMiddlewareSpec
extends
ObjectBehavior
{
public
function
let
()
{
$xsrfValidateRequest
=
function
()
{
/** XSRF::validateRequest() */
return
true
;
};
$this
->
beConstructedWith
(
$xsrfValidateRequest
);
}
public
function
it_is_initializable
()
{
$this
->
shouldHaveType
(
LoggedInMiddleware
::
class
);
}
public
function
it_should_process
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
,
ResponseInterface
$response
,
User
$user
)
{
$request
->
getAttribute
(
'_phpspec_user'
)
->
shouldBeCalled
()
->
willReturn
(
$user
);
$handler
->
handle
(
$request
)
->
shouldBeCalled
()
->
willReturn
(
$response
);
$this
->
setAttributeName
(
'_phpspec_user'
)
->
process
(
$request
,
$handler
)
->
shouldReturn
(
$response
);
}
public
function
it_should_throw_unauthorized_if_no_user_during_process
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
)
{
$request
->
getAttribute
(
'_phpspec_user'
)
->
shouldBeCalled
()
->
willReturn
(
null
);
$handler
->
handle
(
$request
)
->
shouldNotBeCalled
();
$this
->
setAttributeName
(
'_phpspec_user'
)
->
shouldThrow
(
UnauthorizedException
::
class
)
->
duringProcess
(
$request
,
$handler
);
}
public
function
it_should_throw_unauthorized_if_xsrf_check_fail_during_process
(
ServerRequestInterface
$request
,
RequestHandlerInterface
$handler
,
User
$user
)
{
$this
->
beConstructedWith
(
function
()
{
/** XSRF::validateRequest() */
return
false
;
});
$request
->
getAttribute
(
'_phpspec_user'
)
->
shouldBeCalled
()
->
willReturn
(
$user
);
$handler
->
handle
(
$request
)
->
shouldNotBeCalled
();
$this
->
setAttributeName
(
'_phpspec_user'
)
->
shouldThrow
(
UnauthorizedException
::
class
)
->
duringProcess
(
$request
,
$handler
);
}
}
This diff is collapsed.
Spec/Core/Router/PrePsr7/FallbackSpec.php
View file @
c5c09ac1
...
...
@@ -2,14 +2,54 @@
namespace
Spec\Minds\Core\Router\PrePsr7
;
use
Minds\Core\Config
;
use
Minds\Core\Router\PrePsr7\Fallback
;
use
PhpSpec\ObjectBehavior
;
use
Prophecy\Argument
;
class
FallbackSpec
extends
ObjectBehavior
{
/** @var Config */
protected
$config
;
public
function
let
(
Config
$config
)
{
$this
->
config
=
$config
;
$this
->
beConstructedWith
(
$config
);
}
public
function
it_is_initializable
()
{
$this
->
shouldHaveType
(
Fallback
::
class
);
}
public
function
it_should_check_if_should_route
()
{
$this
->
callOnWrappedObject
(
'shouldRoute'
,
[
Fallback
::
ALLOWED
[
0
]
])
->
shouldReturn
(
true
);
$this
->
callOnWrappedObject
(
'shouldRoute'
,
[
Fallback
::
ALLOWED
[
count
(
Fallback
::
ALLOWED
)
-
1
]
])
->
shouldReturn
(
true
);
$this
->
callOnWrappedObject
(
'shouldRoute'
,
[
'~**INVALID FALLBACK ROUTE**~?'
])
->
shouldReturn
(
false
);
}
/**
* The rest of this class is untestable due to the need of
* instantiating PrePsr7\Router class on-the-fly.
*
* That's all, folks!
*/
}
This diff is collapsed.
Spec/Core/Router/RouteSpec.php
View file @
c5c09ac1
...
...
@@ -2,14 +2,129 @@
namespace
Spec\Minds\Core\Router
;
use
Closure
;
use
Exception
;
use
Minds\Core\Di\Ref
;
use
Minds\Core\Router\Registry
;
use
Minds\Core\Router\Route
;
use
PhpSpec\ObjectBehavior
;
use
Prophecy\Argument
;
use
Psr\Http\Server\MiddlewareInterface
;
class
RouteSpec
extends
ObjectBehavior
{
/** @var Registry */
protected
$registry
;
public
function
let
(
Registry
$registry
)
{
$this
->
registry
=
$registry
;
$this
->
beConstructedWith
(
$registry
);
}
public
function
it_is_initializable
()
{
$this
->
shouldHaveType
(
Route
::
class
);
}
public
function
it_should_register
(
Ref
$ref
)
{
$method
=
Route
::
ALLOWED_METHODS
[
0
];
$this
->
registry
->
register
(
$method
,
'phpspec/test/2'
,
$ref
,
[])
->
shouldBeCalled
();
$this
->
register
([
$method
],
'/phpspec/test/2/'
,
$ref
)
->
shouldReturn
(
true
);
}
public
function
it_should_register_with_prefix_and_middleware
(
Ref
$ref
,
MiddlewareInterface
$middleware
)
{
$method
=
Route
::
ALLOWED_METHODS
[
0
];
$this
->
registry
->
register
(
$method
,
'prefix/0/phpspec/test/2'
,
$ref
,
[
$middleware
])
->
shouldBeCalled
();
$this
->
withPrefix
(
'/prefix/0/'
)
->
withMiddleware
([
$middleware
])
->
register
([
$method
],
'/phpspec/test/2/'
,
$ref
)
->
shouldReturn
(
true
);
}
public
function
it_should_throw_if_no_known_method_during_register
(
Ref
$ref
)
{
$method
=
'~**INVALID HTTP METHOD**~'
;
$this
->
registry
->
register
(
$method
,
Argument
::
cetera
())
->
shouldNotBeCalled
();
$this
->
shouldThrow
(
new
Exception
(
'Invalid method'
))
->
duringRegister
([
$method
],
'/phpspec/test/2/'
,
$ref
);
}
public
function
it_should_register_all
(
Ref
$ref
)
{
$this
->
registry
->
register
(
Argument
::
type
(
'string'
),
'phpspec/test/2'
,
$ref
,
[])
->
shouldBeCalledTimes
(
count
(
Route
::
ALLOWED_METHODS
));
$this
->
all
(
'/phpspec/test/2/'
,
$ref
)
->
shouldReturn
(
true
);
}
public
function
it_should_register_get
(
Ref
$ref
)
{
$this
->
registry
->
register
(
'get'
,
'phpspec/test/2'
,
$ref
,
[])
->
shouldBeCalled
();
$this
->
register
([
'get'
],
'/phpspec/test/2/'
,
$ref
)
->
shouldReturn
(
true
);
}
public
function
it_should_register_post
(
Ref
$ref
)
{
$this
->
registry
->
register
(
'post'
,
'phpspec/test/2'
,
$ref
,
[])
->
shouldBeCalled
();
$this
->
register
([
'post'
],
'/phpspec/test/2/'
,
$ref
)
->
shouldReturn
(
true
);
}
public
function
it_should_register_put
(
Ref
$ref
)
{
$this
->
registry
->
register
(
'put'
,
'phpspec/test/2'
,
$ref
,
[])
->
shouldBeCalled
();
$this
->
register
([
'put'
],
'/phpspec/test/2/'
,
$ref
)
->
shouldReturn
(
true
);
}
public
function
it_should_register_delete
(
Ref
$ref
)
{
$this
->
registry
->
register
(
'delete'
,
'phpspec/test/2'
,
$ref
,
[])
->
shouldBeCalled
();
$this
->
register
([
'delete'
],
'/phpspec/test/2/'
,
$ref
)
->
shouldReturn
(
true
);
}
}
This diff is collapsed.
Spec/Core/RouterSpec.php
View file @
c5c09ac1
...
...
@@ -2,12 +2,36 @@
namespace
Spec\Minds\Core
;
use
Minds\Core\Features\Manager
as
Features
;
use
Minds\Core\Router
;
use
Minds\Core\Router\Dispatcher
;
use
Minds\Core\Router\PrePsr7\Fallback
;
use
PhpSpec\ObjectBehavior
;
use
Prophecy\Argument
;
class
RouterSpec
extends
ObjectBehavior
{
/** @var Dispatcher */
protected
$dispatcher
;
/** @var Features */
protected
$features
;
/** @var Fallback */
protected
$fallback
;
public
function
let
(
Dispatcher
$dispatcher
,
Features
$features
,
Fallback
$fallback
)
{
$this
->
dispatcher
=
$dispatcher
;
$this
->
features
=
$features
;
$this
->
fallback
=
$fallback
;
$this
->
beConstructedWith
(
$dispatcher
,
$features
,
$fallback
);
}
public
function
it_is_initializable
()
{
$this
->
shouldHaveType
(
Router
::
class
);
...
...
This diff is collapsed.
Please
register
or
sign in
to comment