Context::should_be_an_array()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php declare(strict_types=1);
2
namespace Behapi\Json;
3
4
use stdClass;
5
use DateTimeImmutable;
6
7
use Throwable;
8
use InvalidArgumentException;
9
10
use Behat\Gherkin\Node\PyStringNode;
11
use Behat\Behat\Context\Context as BehatContext;
12
13
use Symfony\Component\PropertyAccess\PropertyAccess;
14
use Symfony\Component\PropertyAccess\PropertyAccessor;
15
16
use Behapi\Assert\Assert;
17
use Behapi\HttpHistory\History as HttpHistory;
18
19
use function sprintf;
20
use function preg_match;
21
22
use function json_decode;
23
use function json_last_error;
24
use function json_last_error_msg;
25
26
use const JSON_ERROR_NONE;
27
use const PREG_OFFSET_CAPTURE;
28
29
class Context implements BehatContext
30
{
31
    use CountTrait;
32
    use ComparisonTrait;
33
    use EachInCollectionTrait;
34
35
    /** @var HttpHistory */
36
    private $history;
37
38
    /** @var string[] */
39
    private $contentTypes;
40
41
    /** @var PropertyAccessor */
42
    private $accessor;
43
44
    public function __construct(HttpHistory $history, array $contentTypes = ['application/json'])
45
    {
46
        $this->accessor = PropertyAccess::createPropertyAccessor();
47
48
        $this->history = $history;
49
        $this->contentTypes = $contentTypes;
50
    }
51
52
    /** @return mixed */
53
    protected function getValue(?string $path)
54
    {
55
        $json = json_decode((string) $this->history->getLastResponse()->getBody());
56
57
        return $path === null ? $json : $this->accessor->getValue($json, $path);
58
    }
59
60
    /**
61
     * @Then :path should be accessible in the latest json response
62
     * @Then :path should :not exist in the latest json response
63
     */
64
    final public function path_should_be_readable(string $path, ?string $not = null): void
65
    {
66
        $assert = Assert::that($this->accessor->isReadable($this->getValue(null), $path));
67
68
        if ($not !== null) {
69
            $assert = $assert->not();
70
        }
71
72
        $assert->same(true);
73
    }
74
75
    /**
76
     * @Then in the json, :path should be equal to :expected
77
     * @Then in the json, :path should :not be equal to :expected
78
     */
79
    final public function the_json_path_should_be_equal_to(string $path, ?string $not = null, $expected): void
80
    {
81
        $assert = Assert::that($this->getValue($path));
82
83
        if ($not !== null) {
84
            $assert = $assert->not();
85
        }
86
87
        $assert->eq($expected);
88
    }
89
90
    /**
91
     * @Then in the json, :path should be:
92
     * @Then in the json, :path should :not be:
93
     */
94
    final public function the_json_path_should_be_py_string(string $path, ?string $not = null, PyStringNode $expected): void
95
    {
96
        $assert = Assert::that($this->getValue($path));
97
98
        if ($not !== null) {
99
            $assert = $assert->not();
100
        }
101
102
        $assert->same($expected->getRaw());
103
    }
104
105
    /**
106
     * @Then /^in the json, "(?P<path>(?:[^"]|\\")*)" should be (?P<expected>true|false)$/
107
     * @Then /^in the json, "(?P<path>(?:[^"]|\\")*)" should :not be (?P<expected>true|false)$/
108
     */
109
    final public function the_json_path_should_be(string $path, ?string $not = null, string $expected): void
110
    {
111
        $assert = Assert::that($this->getValue($path));
112
113
        if ($not !== null) {
114
            $assert = $assert->not();
115
        }
116
117
        $assert->same($expected === 'true');
118
    }
119
120
    /**
121
     * @Then in the json, :path should be null
122
     * @Then in the json, :path should :not be null
123
     */
124
    final public function the_json_path_should_be_null(string $path, ?string $not = null): void
125
    {
126
        $assert = Assert::that($this->getValue($path));
127
128
        if ($not !== null) {
129
            $assert = $assert->not();
130
        }
131
132
        $assert->null();
133
    }
134
135
    /**
136
     * @Then in the json, :path should be empty
137
     * @Then in the json, :path should :not be empty
138
     */
139
    final public function the_json_path_should_be_empty(string $path, ?string $not = null): void
140
    {
141
        $assert = Assert::that($this->getValue($path));
142
143
        if ($not !== null) {
144
            $assert = $assert->not();
145
        }
146
147
        $assert->empty();
148
    }
149
150
    /**
151
     * @Then in the json, :path should contain :expected
152
     * @Then in the json, :path should :not contain :expected
153
     */
154
    final public function the_json_path_contains(string $path, ?string $not = null, $expected): void
155
    {
156
        $assert = Assert::that($this->getValue($path));
157
158
        if ($not !== null) {
159
            $assert = $assert->not();
160
        }
161
162
        $assert->contains($expected);
163
    }
164
165
    /** @Then in the json, :path collection should contain an element with :value equal to :expected */
166
    final public function the_json_path_collection_contains(string $path, string $value, $expected): void
167
    {
168
        $collection = $this->getValue($path);
169
170
        Assert::that($collection)
171
            ->isTraversable()
172
        ;
173
174
        foreach ($collection as $element) {
175
            if ($expected === $this->accessor->getValue($element, $value)) {
176
                return;
177
            }
178
        }
179
180
        throw new InvalidArgumentException("$path collection does not contain an element with $value equal to $expected");
181
    }
182
183
    /** @Then in the json, :path should be a valid date(time) */
184
    final public function the_json_path_should_be_a_valid_date(string $path): void
185
    {
186
        try {
187
            new DateTimeImmutable($this->getValue($path));
188
        } catch (Throwable $t) {
189
            throw new InvalidArgumentException("$path does not contain a valid date");
190
        }
191
    }
192
193
    /**
194
     * @Then in the json, the root should be an array
195
     * @Then in the json, :path should be an array
196
     */
197
    final public function should_be_an_array(?string $path = null): void
198
    {
199
        Assert::that($this->getValue($path))
200
            ->isArray()
201
        ;
202
    }
203
204
    /** @Then in the json, :path should be a valid json encoded string */
205
    final public function the_json_path_should_be_a_valid_json_encoded_string(string $path): void
206
    {
207
        Assert::that($this->getValue($path))
208
            ->isJsonString()
209
        ;
210
    }
211
212
    /**
213
     * @Then the response should be a valid json response
214
     *
215
     * ---
216
     *
217
     * This method is built-on the default php's json extension. You should
218
     * overwrite it if you want to add supplementary checks or use something
219
     * else instead (such as Seldaek's JsonLint package).
220
     */
221
    public function response_should_be_a_valid_json_response()
222
    {
223
        $this->getValue(null);
224
225
        [$contentType,] = explode(';', $this->history->getLastResponse()->getHeaderLine('Content-Type'), 2);
226
227
        Assert::that(json_last_error())
228
            ->same(JSON_ERROR_NONE, sprintf('The response is not a valid json (%s)', json_last_error_msg()))
229
        ;
230
231
        Assert::that($contentType)
232
            ->choice($this->contentTypes, 'The response should have a valid content-type (expected one of %2$s, got %1$s)')
233
        ;
234
    }
235
236
    /**
237
     *  @Then in the json, :path should match :pattern
238
     *  @Then in the json, :path should :not match :pattern
239
     */
240
    final public function the_json_path_should_match(string $path, ?string $not = null, string $pattern): void
241
    {
242
        $assert = Assert::that($this->getValue($path));
243
244
        if ($not !== null) {
245
            $assert = $assert->not();
246
        }
247
248
        $assert->regex($pattern);
249
    }
250
}
251