3
declare(strict_types=1);
6
* This file is part of CodeIgniter 4 framework.
8
* (c) CodeIgniter Foundation <admin@codeigniter.com>
10
* For the full copyright and license information, please view
11
* the LICENSE file that was distributed with this source code.
14
namespace CodeIgniter\Test;
16
use CodeIgniter\HTTP\RedirectResponse;
17
use CodeIgniter\HTTP\RequestInterface;
18
use CodeIgniter\HTTP\ResponseInterface;
19
use CodeIgniter\I18n\Time;
20
use PHPUnit\Framework\Assert;
21
use PHPUnit\Framework\Constraint\IsEqual;
24
* Consolidated response processing
29
* @see \CodeIgniter\Test\TestResponseTest
36
* @var RequestInterface|null
43
* @var ResponseInterface
55
* Stores or the Response and parses the body in the DOM.
57
public function __construct(ResponseInterface $response)
59
$this->setResponse($response);
62
// --------------------------------------------------------------------
64
// --------------------------------------------------------------------
71
public function setRequest(RequestInterface $request)
73
$this->request = $request;
79
* Sets the Response and updates the DOM.
83
public function setResponse(ResponseInterface $response)
85
$this->response = $response;
86
$this->domParser = new DOMParser();
88
$body = $response->getBody();
90
if (is_string($body) && $body !== '') {
91
$this->domParser->withString($body);
100
* @return RequestInterface|null
102
public function request()
104
return $this->request;
110
* @return ResponseInterface
112
public function response()
114
return $this->response;
117
// --------------------------------------------------------------------
119
// --------------------------------------------------------------------
122
* Boils down the possible responses into a boolean valid/not-valid
125
public function isOK(): bool
127
$status = $this->response->getStatusCode();
129
// Only 200 and 300 range status codes
130
// are considered valid.
131
if ($status >= 400 || $status < 200) {
135
$body = (string) $this->response->getBody();
137
// Empty bodies are not considered valid, unless in redirects
138
return ! ($status < 300 && $body === '');
142
* Asserts that the status is a specific value.
144
public function assertStatus(int $code): void
146
Assert::assertSame($code, $this->response->getStatusCode());
150
* Asserts that the Response is considered OK.
152
public function assertOK(): void
156
"{$this->response->getStatusCode()} is not a successful status code, or Response has an empty body."
161
* Asserts that the Response is considered not OK.
163
public function assertNotOK(): void
167
"{$this->response->getStatusCode()} is an unexpected successful status code, or Response body has content."
171
// --------------------------------------------------------------------
173
// --------------------------------------------------------------------
176
* Returns whether or not the Response was a redirect or RedirectResponse
178
public function isRedirect(): bool
180
return $this->response instanceof RedirectResponse
181
|| $this->response->hasHeader('Location')
182
|| $this->response->hasHeader('Refresh');
186
* Assert that the given response was a redirect.
188
public function assertRedirect(): void
190
Assert::assertTrue($this->isRedirect(), 'Response is not a redirect or instance of RedirectResponse.');
194
* Assert that a given response was a redirect
195
* and it was redirect to a specific URI.
197
public function assertRedirectTo(string $uri): void
199
$this->assertRedirect();
201
$uri = trim(strtolower($uri));
202
$redirectUri = strtolower($this->getRedirectUrl());
204
$matches = $uri === $redirectUri
205
|| strtolower(site_url($uri)) === $redirectUri
206
|| $uri === site_url($redirectUri);
208
Assert::assertTrue($matches, "Redirect URL '{$uri}' does not match '{$redirectUri}'.");
212
* Assert that the given response was not a redirect.
214
public function assertNotRedirect(): void
216
Assert::assertFalse($this->isRedirect(), 'Response is an unexpected redirect or instance of RedirectResponse.');
220
* Returns the URL set for redirection.
222
public function getRedirectUrl(): ?string
224
if (! $this->isRedirect()) {
228
if ($this->response->hasHeader('Location')) {
229
return $this->response->getHeaderLine('Location');
232
if ($this->response->hasHeader('Refresh')) {
233
return str_replace('0;url=', '', $this->response->getHeaderLine('Refresh'));
239
// --------------------------------------------------------------------
241
// --------------------------------------------------------------------
244
* Asserts that an SESSION key has been set and, optionally, test its value.
246
* @param mixed $value
248
public function assertSessionHas(string $key, $value = null): void
250
Assert::assertArrayHasKey($key, $_SESSION, "Key '{$key}' is not in the current \$_SESSION");
252
if ($value === null) {
256
if (is_scalar($value)) {
257
Assert::assertSame($value, $_SESSION[$key], "The value of key '{$key}' ({$value}) does not match expected value.");
262
Assert::assertSame($value, $_SESSION[$key], "The value of key '{$key}' does not match expected value.");
266
* Asserts the session is missing $key.
268
public function assertSessionMissing(string $key): void
270
Assert::assertArrayNotHasKey($key, $_SESSION, "Key '{$key}' should not be present in \$_SESSION.");
273
// --------------------------------------------------------------------
275
// --------------------------------------------------------------------
278
* Asserts that the Response contains a specific header.
280
* @param string|null $value
282
public function assertHeader(string $key, $value = null): void
284
Assert::assertTrue($this->response->hasHeader($key), "Header '{$key}' is not a valid Response header.");
286
if ($value !== null) {
289
$this->response->getHeaderLine($key),
290
"The value of '{$key}' header ({$this->response->getHeaderLine($key)}) does not match expected value."
296
* Asserts the Response headers does not contain the specified header.
298
public function assertHeaderMissing(string $key): void
300
Assert::assertFalse($this->response->hasHeader($key), "Header '{$key}' should not be in the Response headers.");
303
// --------------------------------------------------------------------
305
// --------------------------------------------------------------------
308
* Asserts that the response has the specified cookie.
310
* @param string|null $value
312
public function assertCookie(string $key, $value = null, string $prefix = ''): void
314
Assert::assertTrue($this->response->hasCookie($key, $value, $prefix), "Cookie named '{$key}' is not found.");
318
* Assert the Response does not have the specified cookie set.
320
public function assertCookieMissing(string $key): void
322
Assert::assertFalse($this->response->hasCookie($key), "Cookie named '{$key}' should not be set.");
326
* Asserts that a cookie exists and has an expired time.
328
public function assertCookieExpired(string $key, string $prefix = ''): void
330
Assert::assertTrue($this->response->hasCookie($key, null, $prefix));
332
Assert::assertGreaterThan(
333
Time::now()->getTimestamp(),
334
$this->response->getCookie($key, $prefix)->getExpiresTimestamp()
338
// --------------------------------------------------------------------
340
// --------------------------------------------------------------------
343
* Returns the response's body as JSON
345
* @return false|string
347
public function getJSON()
349
$response = $this->response->getJSON();
351
if ($response === null) {
359
* Test that the response contains a matching JSON fragment.
361
public function assertJSONFragment(array $fragment, bool $strict = false): void
363
$json = json_decode($this->getJSON(), true);
364
Assert::assertIsArray($json, 'Response is not a valid JSON.');
366
$patched = array_replace_recursive($json, $fragment);
369
Assert::assertSame($json, $patched, 'Response does not contain a matching JSON fragment.');
374
Assert::assertThat($patched, new IsEqual($json), 'Response does not contain a matching JSON fragment.');
378
* Asserts that the JSON exactly matches the passed in data.
379
* If the value being passed in is a string, it must be a json_encoded string.
381
* @param array|object|string $test
383
public function assertJSONExact($test): void
385
$json = $this->getJSON();
387
if (is_object($test)) {
388
$test = method_exists($test, 'toArray') ? $test->toArray() : (array) $test;
391
if (is_array($test)) {
392
$test = service('format')->getFormatter('application/json')->format($test);
395
Assert::assertJsonStringEqualsJsonString($test, $json, 'Response does not contain matching JSON.');
398
// --------------------------------------------------------------------
400
// --------------------------------------------------------------------
403
* Returns the response' body as XML
405
* @return bool|string|null
407
public function getXML()
409
return $this->response->getXML();
412
// --------------------------------------------------------------------
414
// --------------------------------------------------------------------
417
* Assert that the desired text can be found in the result body.
419
public function assertSee(?string $search = null, ?string $element = null): void
422
$this->domParser->see($search, $element),
423
"Text '{$search}' is not seen in response."
428
* Asserts that we do not see the specified text.
430
public function assertDontSee(?string $search = null, ?string $element = null): void
433
$this->domParser->dontSee($search, $element),
434
"Text '{$search}' is unexpectedly seen in response."
439
* Assert that we see an element selected via a CSS selector.
441
public function assertSeeElement(string $search): void
444
$this->domParser->seeElement($search),
445
"Element with selector '{$search}' is not seen in response."
450
* Assert that we do not see an element selected via a CSS selector.
452
public function assertDontSeeElement(string $search): void
455
$this->domParser->dontSeeElement($search),
456
"Element with selector '{$search}' is unexpectedly seen in response.'"
461
* Assert that we see a link with the matching text and/or class.
463
public function assertSeeLink(string $text, ?string $details = null): void
466
$this->domParser->seeLink($text, $details),
467
"Anchor tag with text '{$text}' is not seen in response."
472
* Assert that we see an input with name/value.
474
public function assertSeeInField(string $field, ?string $value = null): void
477
$this->domParser->seeInField($field, $value),
478
"Input named '{$field}' with value '{$value}' is not seen in response."
483
* Forward any unrecognized method calls to our DOMParser instance.
485
* @param list<mixed> $params
487
public function __call(string $function, array $params): mixed
489
if (method_exists($this->domParser, $function)) {
490
return $this->domParser->{$function}(...$params);