You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
293 lines
6.7 KiB
293 lines
6.7 KiB
<?php |
|
|
|
/** |
|
* This file is part of CodeIgniter 4 framework. |
|
* |
|
* (c) CodeIgniter Foundation <admin@codeigniter.com> |
|
* |
|
* For the full copyright and license information, please view |
|
* the LICENSE file that was distributed with this source code. |
|
*/ |
|
|
|
namespace CodeIgniter\Test; |
|
|
|
use CodeIgniter\Controller; |
|
use CodeIgniter\HTTP\IncomingRequest; |
|
use CodeIgniter\HTTP\ResponseInterface; |
|
use CodeIgniter\HTTP\URI; |
|
use Config\App; |
|
use Config\Services; |
|
use InvalidArgumentException; |
|
use Psr\Log\LoggerInterface; |
|
use Throwable; |
|
|
|
/** |
|
* ControllerTester Trait |
|
* |
|
* Provides features that make testing controllers simple and fluent. |
|
* |
|
* Example: |
|
* |
|
* $this->withRequest($request) |
|
* ->withResponse($response) |
|
* ->withURI($uri) |
|
* ->withBody($body) |
|
* ->controller('App\Controllers\Home') |
|
* ->execute('methodName'); |
|
* |
|
* @deprecated Use ControllerTestTrait instead |
|
* |
|
* @codeCoverageIgnore |
|
*/ |
|
trait ControllerTester |
|
{ |
|
/** |
|
* Controller configuration. |
|
* |
|
* @var App |
|
*/ |
|
protected $appConfig; |
|
|
|
/** |
|
* Request. |
|
* |
|
* @var IncomingRequest |
|
*/ |
|
protected $request; |
|
|
|
/** |
|
* Response. |
|
* |
|
* @var ResponseInterface |
|
*/ |
|
protected $response; |
|
|
|
/** |
|
* Message logger. |
|
* |
|
* @var LoggerInterface |
|
*/ |
|
protected $logger; |
|
|
|
/** |
|
* Initialized controller. |
|
* |
|
* @var Controller |
|
*/ |
|
protected $controller; |
|
|
|
/** |
|
* URI of this request. |
|
* |
|
* @var string |
|
*/ |
|
protected $uri = 'http://example.com'; |
|
|
|
/** |
|
* Request or response body. |
|
* |
|
* @var string|null |
|
*/ |
|
protected $body; |
|
|
|
/** |
|
* Initializes required components. |
|
*/ |
|
protected function setUpControllerTester(): void |
|
{ |
|
if (empty($this->appConfig)) { |
|
$this->appConfig = config(App::class); |
|
} |
|
|
|
if (! $this->uri instanceof URI) { |
|
$this->uri = Services::uri($this->appConfig->baseURL ?? 'http://example.com/', false); |
|
} |
|
|
|
if (empty($this->request)) { |
|
// Do some acrobatics so we can use the Request service with our own URI |
|
$tempUri = Services::uri(); |
|
Services::injectMock('uri', $this->uri); |
|
|
|
$this->withRequest(Services::incomingrequest($this->appConfig, false)->setBody($this->body)); |
|
|
|
// Restore the URI service |
|
Services::injectMock('uri', $tempUri); |
|
} |
|
|
|
if (empty($this->response)) { |
|
$this->response = Services::response($this->appConfig, false); |
|
} |
|
|
|
if (empty($this->logger)) { |
|
$this->logger = Services::logger(); |
|
} |
|
} |
|
|
|
/** |
|
* Loads the specified controller, and generates any needed dependencies. |
|
* |
|
* @return mixed |
|
*/ |
|
public function controller(string $name) |
|
{ |
|
if (! class_exists($name)) { |
|
throw new InvalidArgumentException('Invalid Controller: ' . $name); |
|
} |
|
|
|
$this->controller = new $name(); |
|
$this->controller->initController($this->request, $this->response, $this->logger); |
|
|
|
return $this; |
|
} |
|
|
|
/** |
|
* Runs the specified method on the controller and returns the results. |
|
* |
|
* @param array $params |
|
* |
|
* @return ControllerResponse |
|
* |
|
* @throws InvalidArgumentException |
|
*/ |
|
public function execute(string $method, ...$params) |
|
{ |
|
if (! method_exists($this->controller, $method) || ! is_callable([$this->controller, $method])) { |
|
throw new InvalidArgumentException('Method does not exist or is not callable in controller: ' . $method); |
|
} |
|
|
|
// The URL helper is always loaded by the system |
|
// so ensure it's available. |
|
helper('url'); |
|
|
|
$result = (new ControllerResponse()) |
|
->setRequest($this->request) |
|
->setResponse($this->response); |
|
|
|
$response = null; |
|
|
|
try { |
|
ob_start(); |
|
|
|
$response = $this->controller->{$method}(...$params); |
|
} catch (Throwable $e) { |
|
$code = $e->getCode(); |
|
|
|
// If code is not a valid HTTP status then assume there is an error |
|
if ($code < 100 || $code >= 600) { |
|
throw $e; |
|
} |
|
|
|
$result->response()->setStatusCode($code); |
|
} finally { |
|
$output = ob_get_clean(); |
|
|
|
// If the controller returned a response, use it |
|
if (isset($response) && $response instanceof ResponseInterface) { |
|
$result->setResponse($response); |
|
} |
|
|
|
// check if controller returned a view rather than echoing it |
|
if (is_string($response)) { |
|
$output = $response; |
|
$result->response()->setBody($output); |
|
$result->setBody($output); |
|
} elseif (! empty($response) && ! empty($response->getBody())) { |
|
$result->setBody($response->getBody()); |
|
} else { |
|
$result->setBody(''); |
|
} |
|
} |
|
|
|
// If not response code has been sent, assume a success |
|
if (empty($result->response()->getStatusCode())) { |
|
$result->response()->setStatusCode(200); |
|
} |
|
|
|
return $result; |
|
} |
|
|
|
/** |
|
* Set controller's config, with method chaining. |
|
* |
|
* @param mixed $appConfig |
|
* |
|
* @return mixed |
|
*/ |
|
public function withConfig($appConfig) |
|
{ |
|
$this->appConfig = $appConfig; |
|
|
|
return $this; |
|
} |
|
|
|
/** |
|
* Set controller's request, with method chaining. |
|
* |
|
* @param mixed $request |
|
* |
|
* @return mixed |
|
*/ |
|
public function withRequest($request) |
|
{ |
|
$this->request = $request; |
|
|
|
// Make sure it's available for other classes |
|
Services::injectMock('request', $request); |
|
|
|
return $this; |
|
} |
|
|
|
/** |
|
* Set controller's response, with method chaining. |
|
* |
|
* @param mixed $response |
|
* |
|
* @return mixed |
|
*/ |
|
public function withResponse($response) |
|
{ |
|
$this->response = $response; |
|
|
|
return $this; |
|
} |
|
|
|
/** |
|
* Set controller's logger, with method chaining. |
|
* |
|
* @param mixed $logger |
|
* |
|
* @return mixed |
|
*/ |
|
public function withLogger($logger) |
|
{ |
|
$this->logger = $logger; |
|
|
|
return $this; |
|
} |
|
|
|
/** |
|
* Set the controller's URI, with method chaining. |
|
* |
|
* @return mixed |
|
*/ |
|
public function withUri(string $uri) |
|
{ |
|
$this->uri = new URI($uri); |
|
|
|
return $this; |
|
} |
|
|
|
/** |
|
* Set the method's body, with method chaining. |
|
* |
|
* @param string|null $body |
|
* |
|
* @return mixed |
|
*/ |
|
public function withBody($body) |
|
{ |
|
$this->body = $body; |
|
|
|
return $this; |
|
} |
|
}
|
|
|