ci4

Форк
0
/
TestResponse.php 
495 строк · 13.5 Кб
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\Test;
15

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;
22

23
/**
24
 * Consolidated response processing
25
 * for test results.
26
 *
27
 * @mixin DOMParser
28
 *
29
 * @see \CodeIgniter\Test\TestResponseTest
30
 */
31
class TestResponse
32
{
33
    /**
34
     * The request.
35
     *
36
     * @var RequestInterface|null
37
     */
38
    protected $request;
39

40
    /**
41
     * The response.
42
     *
43
     * @var ResponseInterface
44
     */
45
    protected $response;
46

47
    /**
48
     * DOM for the body.
49
     *
50
     * @var DOMParser
51
     */
52
    protected $domParser;
53

54
    /**
55
     * Stores or the Response and parses the body in the DOM.
56
     */
57
    public function __construct(ResponseInterface $response)
58
    {
59
        $this->setResponse($response);
60
    }
61

62
    // --------------------------------------------------------------------
63
    // Getters / Setters
64
    // --------------------------------------------------------------------
65

66
    /**
67
     * Sets the request.
68
     *
69
     * @return $this
70
     */
71
    public function setRequest(RequestInterface $request)
72
    {
73
        $this->request = $request;
74

75
        return $this;
76
    }
77

78
    /**
79
     * Sets the Response and updates the DOM.
80
     *
81
     * @return $this
82
     */
83
    public function setResponse(ResponseInterface $response)
84
    {
85
        $this->response  = $response;
86
        $this->domParser = new DOMParser();
87

88
        $body = $response->getBody();
89

90
        if (is_string($body) && $body !== '') {
91
            $this->domParser->withString($body);
92
        }
93

94
        return $this;
95
    }
96

97
    /**
98
     * Request accessor.
99
     *
100
     * @return RequestInterface|null
101
     */
102
    public function request()
103
    {
104
        return $this->request;
105
    }
106

107
    /**
108
     * Response accessor.
109
     *
110
     * @return ResponseInterface
111
     */
112
    public function response()
113
    {
114
        return $this->response;
115
    }
116

117
    // --------------------------------------------------------------------
118
    // Status Checks
119
    // --------------------------------------------------------------------
120

121
    /**
122
     * Boils down the possible responses into a boolean valid/not-valid
123
     * response type.
124
     */
125
    public function isOK(): bool
126
    {
127
        $status = $this->response->getStatusCode();
128

129
        // Only 200 and 300 range status codes
130
        // are considered valid.
131
        if ($status >= 400 || $status < 200) {
132
            return false;
133
        }
134

135
        $body = (string) $this->response->getBody();
136

137
        // Empty bodies are not considered valid, unless in redirects
138
        return ! ($status < 300 && $body === '');
139
    }
140

141
    /**
142
     * Asserts that the status is a specific value.
143
     */
144
    public function assertStatus(int $code): void
145
    {
146
        Assert::assertSame($code, $this->response->getStatusCode());
147
    }
148

149
    /**
150
     * Asserts that the Response is considered OK.
151
     */
152
    public function assertOK(): void
153
    {
154
        Assert::assertTrue(
155
            $this->isOK(),
156
            "{$this->response->getStatusCode()} is not a successful status code, or Response has an empty body."
157
        );
158
    }
159

160
    /**
161
     * Asserts that the Response is considered not OK.
162
     */
163
    public function assertNotOK(): void
164
    {
165
        Assert::assertFalse(
166
            $this->isOK(),
167
            "{$this->response->getStatusCode()} is an unexpected successful status code, or Response body has content."
168
        );
169
    }
170

171
    // --------------------------------------------------------------------
172
    // Redirection
173
    // --------------------------------------------------------------------
174

175
    /**
176
     * Returns whether or not the Response was a redirect or RedirectResponse
177
     */
178
    public function isRedirect(): bool
179
    {
180
        return $this->response instanceof RedirectResponse
181
            || $this->response->hasHeader('Location')
182
            || $this->response->hasHeader('Refresh');
183
    }
184

185
    /**
186
     * Assert that the given response was a redirect.
187
     */
188
    public function assertRedirect(): void
189
    {
190
        Assert::assertTrue($this->isRedirect(), 'Response is not a redirect or instance of RedirectResponse.');
191
    }
192

193
    /**
194
     * Assert that a given response was a redirect
195
     * and it was redirect to a specific URI.
196
     */
197
    public function assertRedirectTo(string $uri): void
198
    {
199
        $this->assertRedirect();
200

201
        $uri         = trim(strtolower($uri));
202
        $redirectUri = strtolower($this->getRedirectUrl());
203

204
        $matches = $uri === $redirectUri
205
            || strtolower(site_url($uri)) === $redirectUri
206
            || $uri === site_url($redirectUri);
207

208
        Assert::assertTrue($matches, "Redirect URL '{$uri}' does not match '{$redirectUri}'.");
209
    }
210

211
    /**
212
     * Assert that the given response was not a redirect.
213
     */
214
    public function assertNotRedirect(): void
215
    {
216
        Assert::assertFalse($this->isRedirect(), 'Response is an unexpected redirect or instance of RedirectResponse.');
217
    }
218

219
    /**
220
     * Returns the URL set for redirection.
221
     */
222
    public function getRedirectUrl(): ?string
223
    {
224
        if (! $this->isRedirect()) {
225
            return null;
226
        }
227

228
        if ($this->response->hasHeader('Location')) {
229
            return $this->response->getHeaderLine('Location');
230
        }
231

232
        if ($this->response->hasHeader('Refresh')) {
233
            return str_replace('0;url=', '', $this->response->getHeaderLine('Refresh'));
234
        }
235

236
        return null;
237
    }
238

239
    // --------------------------------------------------------------------
240
    // Session
241
    // --------------------------------------------------------------------
242

243
    /**
244
     * Asserts that an SESSION key has been set and, optionally, test its value.
245
     *
246
     * @param mixed $value
247
     */
248
    public function assertSessionHas(string $key, $value = null): void
249
    {
250
        Assert::assertArrayHasKey($key, $_SESSION, "Key '{$key}' is not in the current \$_SESSION");
251

252
        if ($value === null) {
253
            return;
254
        }
255

256
        if (is_scalar($value)) {
257
            Assert::assertSame($value, $_SESSION[$key], "The value of key '{$key}' ({$value}) does not match expected value.");
258

259
            return;
260
        }
261

262
        Assert::assertSame($value, $_SESSION[$key], "The value of key '{$key}' does not match expected value.");
263
    }
264

265
    /**
266
     * Asserts the session is missing $key.
267
     */
268
    public function assertSessionMissing(string $key): void
269
    {
270
        Assert::assertArrayNotHasKey($key, $_SESSION, "Key '{$key}' should not be present in \$_SESSION.");
271
    }
272

273
    // --------------------------------------------------------------------
274
    // Headers
275
    // --------------------------------------------------------------------
276

277
    /**
278
     * Asserts that the Response contains a specific header.
279
     *
280
     * @param string|null $value
281
     */
282
    public function assertHeader(string $key, $value = null): void
283
    {
284
        Assert::assertTrue($this->response->hasHeader($key), "Header '{$key}' is not a valid Response header.");
285

286
        if ($value !== null) {
287
            Assert::assertSame(
288
                $value,
289
                $this->response->getHeaderLine($key),
290
                "The value of '{$key}' header ({$this->response->getHeaderLine($key)}) does not match expected value."
291
            );
292
        }
293
    }
294

295
    /**
296
     * Asserts the Response headers does not contain the specified header.
297
     */
298
    public function assertHeaderMissing(string $key): void
299
    {
300
        Assert::assertFalse($this->response->hasHeader($key), "Header '{$key}' should not be in the Response headers.");
301
    }
302

303
    // --------------------------------------------------------------------
304
    // Cookies
305
    // --------------------------------------------------------------------
306

307
    /**
308
     * Asserts that the response has the specified cookie.
309
     *
310
     * @param string|null $value
311
     */
312
    public function assertCookie(string $key, $value = null, string $prefix = ''): void
313
    {
314
        Assert::assertTrue($this->response->hasCookie($key, $value, $prefix), "Cookie named '{$key}' is not found.");
315
    }
316

317
    /**
318
     * Assert the Response does not have the specified cookie set.
319
     */
320
    public function assertCookieMissing(string $key): void
321
    {
322
        Assert::assertFalse($this->response->hasCookie($key), "Cookie named '{$key}' should not be set.");
323
    }
324

325
    /**
326
     * Asserts that a cookie exists and has an expired time.
327
     */
328
    public function assertCookieExpired(string $key, string $prefix = ''): void
329
    {
330
        Assert::assertTrue($this->response->hasCookie($key, null, $prefix));
331

332
        Assert::assertGreaterThan(
333
            Time::now()->getTimestamp(),
334
            $this->response->getCookie($key, $prefix)->getExpiresTimestamp()
335
        );
336
    }
337

338
    // --------------------------------------------------------------------
339
    // JSON
340
    // --------------------------------------------------------------------
341

342
    /**
343
     * Returns the response's body as JSON
344
     *
345
     * @return false|string
346
     */
347
    public function getJSON()
348
    {
349
        $response = $this->response->getJSON();
350

351
        if ($response === null) {
352
            return false;
353
        }
354

355
        return $response;
356
    }
357

358
    /**
359
     * Test that the response contains a matching JSON fragment.
360
     */
361
    public function assertJSONFragment(array $fragment, bool $strict = false): void
362
    {
363
        $json = json_decode($this->getJSON(), true);
364
        Assert::assertIsArray($json, 'Response is not a valid JSON.');
365

366
        $patched = array_replace_recursive($json, $fragment);
367

368
        if ($strict) {
369
            Assert::assertSame($json, $patched, 'Response does not contain a matching JSON fragment.');
370

371
            return;
372
        }
373

374
        Assert::assertThat($patched, new IsEqual($json), 'Response does not contain a matching JSON fragment.');
375
    }
376

377
    /**
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.
380
     *
381
     * @param array|object|string $test
382
     */
383
    public function assertJSONExact($test): void
384
    {
385
        $json = $this->getJSON();
386

387
        if (is_object($test)) {
388
            $test = method_exists($test, 'toArray') ? $test->toArray() : (array) $test;
389
        }
390

391
        if (is_array($test)) {
392
            $test = service('format')->getFormatter('application/json')->format($test);
393
        }
394

395
        Assert::assertJsonStringEqualsJsonString($test, $json, 'Response does not contain matching JSON.');
396
    }
397

398
    // --------------------------------------------------------------------
399
    // XML Methods
400
    // --------------------------------------------------------------------
401

402
    /**
403
     * Returns the response' body as XML
404
     *
405
     * @return bool|string|null
406
     */
407
    public function getXML()
408
    {
409
        return $this->response->getXML();
410
    }
411

412
    // --------------------------------------------------------------------
413
    // DomParser
414
    // --------------------------------------------------------------------
415

416
    /**
417
     * Assert that the desired text can be found in the result body.
418
     */
419
    public function assertSee(?string $search = null, ?string $element = null): void
420
    {
421
        Assert::assertTrue(
422
            $this->domParser->see($search, $element),
423
            "Text '{$search}' is not seen in response."
424
        );
425
    }
426

427
    /**
428
     * Asserts that we do not see the specified text.
429
     */
430
    public function assertDontSee(?string $search = null, ?string $element = null): void
431
    {
432
        Assert::assertTrue(
433
            $this->domParser->dontSee($search, $element),
434
            "Text '{$search}' is unexpectedly seen in response."
435
        );
436
    }
437

438
    /**
439
     * Assert that we see an element selected via a CSS selector.
440
     */
441
    public function assertSeeElement(string $search): void
442
    {
443
        Assert::assertTrue(
444
            $this->domParser->seeElement($search),
445
            "Element with selector '{$search}' is not seen in response."
446
        );
447
    }
448

449
    /**
450
     * Assert that we do not see an element selected via a CSS selector.
451
     */
452
    public function assertDontSeeElement(string $search): void
453
    {
454
        Assert::assertTrue(
455
            $this->domParser->dontSeeElement($search),
456
            "Element with selector '{$search}' is unexpectedly seen in response.'"
457
        );
458
    }
459

460
    /**
461
     * Assert that we see a link with the matching text and/or class.
462
     */
463
    public function assertSeeLink(string $text, ?string $details = null): void
464
    {
465
        Assert::assertTrue(
466
            $this->domParser->seeLink($text, $details),
467
            "Anchor tag with text '{$text}' is not seen in response."
468
        );
469
    }
470

471
    /**
472
     * Assert that we see an input with name/value.
473
     */
474
    public function assertSeeInField(string $field, ?string $value = null): void
475
    {
476
        Assert::assertTrue(
477
            $this->domParser->seeInField($field, $value),
478
            "Input named '{$field}' with value '{$value}' is not seen in response."
479
        );
480
    }
481

482
    /**
483
     * Forward any unrecognized method calls to our DOMParser instance.
484
     *
485
     * @param list<mixed> $params
486
     */
487
    public function __call(string $function, array $params): mixed
488
    {
489
        if (method_exists($this->domParser, $function)) {
490
            return $this->domParser->{$function}(...$params);
491
        }
492

493
        return null;
494
    }
495
}
496

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.