Passed
Pull Request — 1.x (#28)
by Kevin
02:58 queued 48s
created

BrowserKitBrowser::json()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 7
rs 10
1
<?php
2
3
namespace Zenstruck\Browser;
4
5
use PHPUnit\Framework\Assert as PHPUnit;
0 ignored issues
show
Bug introduced by
The type PHPUnit\Framework\Assert was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use Symfony\Component\BrowserKit\AbstractBrowser;
7
use Symfony\Component\HttpKernel\Profiler\Profile;
8
use Zenstruck\Browser;
9
use Zenstruck\Browser\Mink\BrowserKitDriver;
10
use Zenstruck\Browser\Response\JsonResponse;
11
12
/**
13
 * @author Kevin Bond <[email protected]>
14
 */
15
abstract class BrowserKitBrowser extends Browser
16
{
17
    private AbstractBrowser $inner;
18
    private ?HttpOptions $defaultHttpOptions = null;
19
20
    public function __construct(AbstractBrowser $inner)
21
    {
22
        $this->inner = $inner;
23
24
        parent::__construct(new BrowserKitDriver($inner));
25
    }
26
27
    /**
28
     * @return static
29
     */
30
    final public function interceptRedirects(): self
31
    {
32
        $this->inner->followRedirects(false);
33
34
        return $this;
35
    }
36
37
    /**
38
     * @return static
39
     */
40
    final public function followRedirects(): self
41
    {
42
        $this->inner->followRedirects(true);
43
44
        if ($this->minkSession()->isStarted() && $this->response()->isRedirect()) {
45
            $this->followRedirect();
46
        }
47
48
        return $this;
49
    }
50
51
    /**
52
     * @param int $max The maximum number of redirects to follow (defaults to "infinite")
53
     *
54
     * @return static
55
     */
56
    final public function followRedirect(int $max = \PHP_INT_MAX): self
57
    {
58
        for ($i = 0; $i < $max; ++$i) {
59
            if (!$this->response()->isRedirect()) {
60
                break;
61
            }
62
63
            $this->inner->followRedirect();
64
        }
65
66
        return $this;
67
    }
68
69
    /**
70
     * @param int $max The maximum number of redirects to follow (defaults to "infinite")
71
     *
72
     * @return static
73
     */
74
    final public function assertRedirectedTo(string $expected, int $max = \PHP_INT_MAX): self
75
    {
76
        $this->assertRedirected();
77
        $this->followRedirect($max);
78
        $this->assertOn($expected);
79
80
        return $this;
81
    }
82
83
    /**
84
     * @param HttpOptions|array $options
85
     *
86
     * @return static
87
     */
88
    final public function setDefaultHttpOptions($options): self
89
    {
90
        $this->defaultHttpOptions = HttpOptions::create($options);
91
92
        return $this;
93
    }
94
95
    /**
96
     * @param HttpOptions|array $options HttpOptions::DEFAULT_OPTIONS
97
     *
98
     * @return static
99
     */
100
    final public function request(string $method, string $url, $options = []): self
101
    {
102
        if ($this->defaultHttpOptions) {
103
            $options = $this->defaultHttpOptions->merge($options);
104
        }
105
106
        $options = HttpOptions::create($options);
107
108
        $this->inner->request($method, $url, $options->query(), $options->files(), $options->server(), $options->body());
109
110
        return $this;
111
    }
112
113
    /**
114
     * @see request()
115
     *
116
     * @return static
117
     */
118
    final public function get(string $url, $options = []): self
119
    {
120
        return $this->request('GET', $url, $options);
121
    }
122
123
    /**
124
     * @see request()
125
     *
126
     * @return static
127
     */
128
    final public function post(string $url, $options = []): self
129
    {
130
        return $this->request('POST', $url, $options);
131
    }
132
133
    /**
134
     * @see request()
135
     *
136
     * @return static
137
     */
138
    final public function put(string $url, $options = []): self
139
    {
140
        return $this->request('PUT', $url, $options);
141
    }
142
143
    /**
144
     * @see request()
145
     *
146
     * @return static
147
     */
148
    final public function delete(string $url, $options = []): self
149
    {
150
        return $this->request('DELETE', $url, $options);
151
    }
152
153
    /**
154
     * @return static
155
     */
156
    final public function assertStatus(int $expected): self
157
    {
158
        return $this->wrapMinkExpectation(
159
            fn() => $this->webAssert()->statusCodeEquals($expected)
160
        );
161
    }
162
163
    /**
164
     * @return static
165
     */
166
    final public function assertSuccessful(): self
167
    {
168
        PHPUnit::assertTrue($this->response()->isSuccessful(), "Expected successful status code (2xx), [{$this->response()->statusCode()}] received.");
169
170
        return $this;
171
    }
172
173
    /**
174
     * @return static
175
     */
176
    final public function assertRedirected(): self
177
    {
178
        if ($this->inner->isFollowingRedirects()) {
179
            throw new \RuntimeException('Cannot assert redirected if not intercepting redirects. Call ->interceptRedirects() before making the request.');
180
        }
181
182
        PHPUnit::assertTrue($this->response()->isRedirect(), "Expected redirect status code (3xx), [{$this->response()->statusCode()}] received.");
183
184
        return $this;
185
    }
186
187
    /**
188
     * @return static
189
     */
190
    final public function assertHeaderEquals(string $header, string $expected): self
191
    {
192
        return $this->wrapMinkExpectation(
193
            fn() => $this->webAssert()->responseHeaderEquals($header, $expected)
194
        );
195
    }
196
197
    /**
198
     * @return static
199
     */
200
    final public function assertHeaderContains(string $header, string $expected): self
201
    {
202
        return $this->wrapMinkExpectation(
203
            fn() => $this->webAssert()->responseHeaderContains($header, $expected)
204
        );
205
    }
206
207
    /**
208
     * @return static
209
     */
210
    final public function assertJson(): self
211
    {
212
        return $this->wrapMinkExpectation(
213
            fn() => $this->webAssert()->responseHeaderContains('Content-Type', 'application/json')
214
        );
215
    }
216
217
    /**
218
     * @param string $expression JMESPath expression
219
     * @param mixed  $expected
220
     *
221
     * @return static
222
     */
223
    final public function assertJsonMatches(string $expression, $expected): self
224
    {
225
        if (!$this->response() instanceof JsonResponse) {
226
            PHPUnit::fail('Not a json response.');
227
        }
228
229
        PHPUnit::assertSame($expected, $this->response()->search($expression));
0 ignored issues
show
Bug introduced by
The method search() does not exist on Zenstruck\Browser\Response. It seems like you code against a sub-type of Zenstruck\Browser\Response such as Zenstruck\Browser\Response\JsonResponse. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

229
        PHPUnit::assertSame($expected, $this->response()->/** @scrutinizer ignore-call */ search($expression));
Loading history...
230
231
        return $this;
232
    }
233
234
    /**
235
     * @return mixed The response content json decoded
236
     */
237
    final public function json()
238
    {
239
        if (!$this->response() instanceof JsonResponse) {
240
            throw new \RuntimeException('Response is not json.');
241
        }
242
243
        return $this->response()->json();
244
    }
245
246
    abstract public function profile(): Profile;
247
248
    /**
249
     * @internal
250
     */
251
    final protected function inner(): AbstractBrowser
252
    {
253
        return $this->inner;
254
    }
255
}
256