Passed
Pull Request — master (#52)
by Baptiste
01:52
created

AbstractContext   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 281
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 53
eloc 77
dl 0
loc 281
rs 6.96
c 0
b 0
f 0

26 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A path_should_not_be_readable() 0 3 1
A getValue() 0 3 1
A the_json_path_should_be_py_string() 0 3 1
A path_should_be_readable() 0 3 1
A the_json_path_should_be_greater_than() 0 3 1
A the_json_path_should_be_less_than() 0 3 1
A the_json_path_should_be_less_or_equal_than() 0 3 1
A the_json_path_should_be_greater_or_equal_than() 0 3 1
A the_json_path_should_be_a_valid_date() 0 6 2
A should_be_an_array() 0 3 2
A the_json_each_elements_in_collection_should_contain() 0 10 4
A the_json_path_elements_in_collection_should_have_a_property() 0 6 3
A the_json_path_should_be_equal_to() 0 6 2
A the_json_each_elements_in_collection_should_be_equal_to() 0 10 4
A the_json_path_should_be_null() 0 6 2
A the_json_path_should_be_a_valid_json_encoded_string() 0 6 1
A the_json_each_elements_in_collection_should_be_bool() 0 10 4
A the_json_each_elements_in_collection_should_be_equal_to_int() 0 10 4
A the_json_path_should_be_empty() 0 6 2
A the_json_path_should_be() 0 6 2
A the_json_path_collection_contains() 0 12 3
A the_json_path_should_not_match() 0 8 2
A the_json_path_contains() 0 6 2
A the_json_path_should_match() 0 3 1
A the_json_path_should_have_elements() 0 14 4

How to fix   Complexity   

Complex Class

Complex classes like AbstractContext often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractContext, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
namespace Behapi\Json;
3
4
use stdClass;
5
use DateTime;
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 Webmozart\Assert\Assert;
17
18
use function preg_match;
19
use function json_last_error;
20
21
use const JSON_ERROR_NONE;
22
use const PREG_OFFSET_CAPTURE;
23
24
abstract class AbstractContext implements BehatContext
25
{
26
    /** @var PropertyAccessor */
27
    private $accessor;
28
29
    public function __construct()
30
    {
31
        $this->accessor = PropertyAccess::createPropertyAccessor();
32
    }
33
34
    abstract protected function getJson();
35
36
    protected function getValue(string $path)
37
    {
38
        return $this->accessor->getValue($this->getJson(), $path);
39
    }
40
41
    /** @Then :path should be accessible in the latest json response */
42
    final public function path_should_be_readable(string $path): void
43
    {
44
        Assert::true($this->accessor->isReadable($this->getJson(), $path), "The path $path should be a valid path");
45
    }
46
47
    /** @Then :path should not exist in the latest json response */
48
    final public function path_should_not_be_readable(string $path): void
49
    {
50
        Assert::false($this->accessor->isReadable($this->getJson(), $path), "The path $path should not be a valid path");
51
    }
52
53
    /**
54
     * @Then in the json, :path should be equal to :expected
55
     * @Then in the json, :path should :not be equal to :expected
56
     */
57
    final public function the_json_path_should_be_equal_to(string $path, ?string $not = null, $expected): void
58
    {
59
        $assert = [Assert::class, $not !== null ? 'notEq' : 'eq'];
60
        assert(is_callable($assert));
61
62
        $assert($this->getValue($path), $expected);
63
    }
64
65
    /** @Then in the json, :path should be: */
66
    final public function the_json_path_should_be_py_string(string $path, PyStringNode $expected): void
67
    {
68
        Assert::same($this->getValue($path), $expected->getRaw());
69
    }
70
71
    /**
72
     * @Then /^in the json, "(?P<path>(?:[^"]|\\")*)" should be (?P<expected>true|false)$/
73
     * @Then /^in the json, "(?P<path>(?:[^"]|\\")*)" should :not be (?P<expected>true|false)$/
74
     */
75
    final public function the_json_path_should_be(string $path, ?string $not = null, string $expected): void
76
    {
77
        $assert = [Assert::class, $not !== null ? 'notSame' : 'same'];
78
        assert(is_callable($assert));
79
80
        $assert($this->getValue($path), 'true' === $expected);
81
    }
82
83
    /**
84
     * @Then in the json, :path should be null
85
     * @Then in the json, :path should :not be null
86
     */
87
    final public function the_json_path_should_be_null(string $path, ?string $not = null): void
88
    {
89
        $assert = [Assert::class, $not !== null ? 'notNull' : 'null'];
90
        assert(is_callable($assert));
91
92
        $assert($this->getValue($path));
93
    }
94
95
    /**
96
     * @Then in the json, :path should be empty
97
     * @Then in the json, :path should :not be empty
98
     */
99
    final public function the_json_path_should_be_empty(string $path, ?string $not = null): void
100
    {
101
        $assert = [Assert::class, $not !== null ? 'notEmpty' : 'isEmpty'];
102
        assert(is_callable($assert));
103
104
        $assert($this->getValue($path));
105
    }
106
107
    /**
108
     * @Then in the json, :path should contain :expected
109
     * @Then in the json, :path should :not contain :expected
110
     */
111
    final public function the_json_path_contains(string $path, ?string $not = null, $expected): void
112
    {
113
        $assert = [Assert::class, $not !== null ? 'notContains' : 'contains'];
114
        assert(is_callable($assert));
115
116
        $assert($this->getValue($path), $expected);
117
    }
118
119
    /** @Then in the json, :path collection should contain an element with :value equal to :expected */
120
    final public function the_json_path_collection_contains(string $path, string $value, $expected): void
121
    {
122
        $collection = $this->accessor->getValue($this->getJson(), $path);
123
        Assert::isIterable($collection);
124
125
        foreach ($collection as $element) {
126
            if ($expected === $this->accessor->getValue($element, $value)) {
127
                return;
128
            }
129
        }
130
131
        throw new InvalidArgumentException("$path collection does not contain an element with $value equal to $expected");
132
    }
133
134
    /** @Then in the json, :path should be a valid date(time) */
135
    final public function the_json_path_should_be_a_valid_date(string $path): void
136
    {
137
        try {
138
            new DateTime($this->getValue($path));
139
        } catch (Throwable $t) {
140
            throw new InvalidArgumentException("$path does not contain a valid date");
141
        }
142
    }
143
144
    /** @Then in the json, :path should be greater than :expected */
145
    final public function the_json_path_should_be_greater_than(string $path, int $expected): void
146
    {
147
        Assert::greaterThan($this->getValue($path), $expected);
148
    }
149
150
    /** @Then in the json, :path should be greater than or equal to :expected */
151
    final public function the_json_path_should_be_greater_or_equal_than(string $path, int $expected): void
152
    {
153
        Assert::greaterThanEq($this->getValue($path), $expected);
154
    }
155
156
    /** @Then in the json, :path should be less than :expected */
157
    final public function the_json_path_should_be_less_than(string $path, int $expected): void
158
    {
159
        Assert::lessThan($this->getValue($path), $expected);
160
    }
161
162
    /** @Then in the json, :path should be less than or equal to :expected */
163
    final public function the_json_path_should_be_less_or_equal_than(string $path, int $expected): void
164
    {
165
        Assert::lessThanEq($this->getValue($path), $expected);
166
    }
167
168
    /** @Then in the json, (?:root|\"(?P<path>(?:[^"]|\\")*)\") should be an array */
169
    final public function should_be_an_array(string $path): void
170
    {
171
        Assert::isArray(empty($path) ? $this->getJson() : $this->getValue($path));
172
    }
173
174
    /**
175
     * @Then in the json, (?:root|\"(?P<path>(?:[^"]|\\")*)\") collection should have (?P<count>[1-9][0-9]*) element(s)
176
     * @Then in the json, (?:root|\"(?P<path>(?:[^"]|\\")*)\") collection should have at (?P<limit>least|most) (?P<count>[1-9][0-9]*) element(s)
177
     */
178
    final public function the_json_path_should_have_elements(string $path, ?string $limit = null, int $count): void
179
    {
180
        $value = empty($path) ? $this->getJson() : $this->getValue($path);
181
182
        $assert = [Assert::class, 'count'];
183
184
        if ($limit !== null) {
185
            $assert[1] = $limit === 'most' ? 'maxCount' : 'minCount';
186
        }
187
188
        assert(is_callable($assert));
189
190
        Assert::isCountable($value);
191
        $assert($value, $count);
192
    }
193
194
    /** @Then in the json, :path should match :pattern */
195
    final public function the_json_path_should_match(string $path, string $pattern): void
196
    {
197
        Assert::regex($this->getValue($path), $pattern);
198
    }
199
200
    /**
201
     * @Then in the json, :path should not match :pattern
202
     *
203
     * -----
204
     *
205
     * Note :: The body of this assertion should be replaced by a
206
     * `Assert::notRegex` as soon as the Assert's PR
207
     * https://github.com/webmozart/assert/pull/58 is merged and released.
208
     */
209
    final public function the_json_path_should_not_match(string $path, string $pattern): void
210
    {
211
        if (!preg_match($pattern, $this->getValue($path), $matches, PREG_OFFSET_CAPTURE)) {
212
            // it's all good, it is supposed not to match. :}
213
            return;
214
        }
215
216
        throw new InvalidArgumentException("The value matches {$pattern} at offset {$matches[0][1]}");
217
    }
218
219
    /** @Then in the json, :path should be a valid json encoded string */
220
    final public function the_json_path_should_be_a_valid_json_encoded_string(string $path): void
221
    {
222
        $value = json_decode($this->getValue($path));
223
224
        Assert::notNull($value);
225
        Assert::same(json_last_error(), JSON_ERROR_NONE);
226
    }
227
228
    /**
229
     * @Then /^in the json, all the elements in the (?:root|\"(?P<path>(?:[^"]|\\")*)\") collection should have a(?:n?) "(?P<property>(?:[^"]|\\")*)" property$/
230
     * @Then /^in the json, all the elements in the (?:root|\"(?P<path>(?:[^"]|\\")*)\") collection should (?P<not>not) have a(?:n?) "(?P<property>(?:[^"]|\\")*)" property$/
231
     **/
232
    final public function the_json_path_elements_in_collection_should_have_a_property(string $path, ?string $not = null, string $property): void
233
    {
234
        $assert = [Assert::class, $not !== null ? 'allPropertyNotExists' : 'allPropertyExists'];
235
        assert(is_callable($assert));
236
237
        $assert(empty($path) ? $this->getJson() : $this->getValue($path), $property);
238
    }
239
240
    /**
241
     * @Then /^in the json, each "(?P<property>(?:[^"]|\\")*)" property in the (?:root|\"(?P<path>(?:[^"]|\\")*)\") collection should be equal to "(?P<expected>(?:[^"]|\\")*)"$/
242
     * @Then /^in the json, each "(?P<property>(?:[^"]|\\")*)" property in the (?:root|\"(?P<path>(?:[^"]|\\")*)\") collection should (?P<not>not) be equal to "(?P<expected>(?:[^"]|\\")*)"$/
243
     */
244
    final public function the_json_each_elements_in_collection_should_be_equal_to(string $path, string $property, ?string $not = null, string $expected): void
245
    {
246
        $values = empty($path) ? $this->getJson() : $this->getValue($path);
247
        Assert::isIterable($values);
248
249
        $assert = [Assert::class, $not !== null ? 'notSame' : 'same'];
250
        assert(is_callable($assert));
251
252
        foreach ($values as $element) {
253
            $assert($this->accessor->getValue($element, $property), $expected);
254
        }
255
    }
256
257
    /**
258
     * @Then /^in the json, each "(?P<property>(?:[^"]|\\")*)" property in the (?:root|\"(?P<path>(?:[^"]|\\")*)\") collection should be (?P<expected>true|false)$/
259
     * @Then /^in the json, each "(?P<property>(?:[^"]|\\")*)" property in the (?:root|\"(?P<path>(?:[^"]|\\")*)\") collection should (?P<not>not) be (?P<expected>true|false)$/
260
     */
261
    final public function the_json_each_elements_in_collection_should_be_bool(string $path, string $property, ?string $not = null, string $expected): void
262
    {
263
        $values = empty($path) ? $this->getJson() : $this->getValue($path);
264
        Assert::isIterable($values);
265
266
        $assert = [Assert::class, $not !== null ? 'notSame' : 'same'];
267
        assert(is_callable($assert));
268
269
        foreach ($values as $element) {
270
            $assert($this->accessor->getValue($element, $property), $expected === 'true');
271
        }
272
    }
273
274
    /**
275
     * @Then /^in the json, each "(?P<property>(?:[^"]|\\")*)" property in the (?:root|\"(?P<path>(?:[^"]|\\")*)\") collection should be equal to (?P<expected>[0-9]+)$/
276
     * @Then /^in the json, each "(?P<property>(?:[^"]|\\")*)" property in the (?:root|\"(?P<path>(?:[^"]|\\")*)\") collection should (?P<not>not) be equal to (?P<expected>[0-9]+)$/
277
     */
278
    final public function the_json_each_elements_in_collection_should_be_equal_to_int(string $path, string $property, ?string $not = null, int $expected): void
279
    {
280
        $values = empty($path) ? $this->getJson() : $this->getValue($path);
281
        Assert::isIterable($values);
282
283
        $assert = [Assert::class, $not !== null ? 'notSame' : 'same'];
284
        assert(is_callable($assert));
285
286
        foreach ($values as $element) {
287
            $assert($this->accessor->getValue($element, $property), $expected);
288
        }
289
    }
290
291
    /**
292
     * @Then /^in the json, each "(?P<property>(?:[^"]|\\")*)" property in the (?:root|\"(?P<path>(?:[^"]|\\")*)\") collection should contain "(?P<expected>(?:[^"]|\\")*)"$/
293
     * @Then /^in the json, each "(?P<property>(?:[^"]|\\")*)" property in the (?:root|\"(?P<path>(?:[^"]|\\")*)\") collection should (?P<not>not) contain "(?P<expected>(?:[^"]|\\")*)"$/
294
     **/
295
    final public function the_json_each_elements_in_collection_should_contain(string $path, string $property, ?string $not = null, string $expected): void
296
    {
297
        $values = empty($path) ? $this->getJson() : $this->getValue($path);
298
        Assert::isIterable($values);
299
300
        $assert = [Assert::class, $not !== null ? 'notSame' : 'same'];
301
        assert(is_callable($assert));
302
303
        foreach ($values as $element) {
304
            $assert($this->accessor->getValue($element, $property), $expected);
305
        }
306
    }
307
}
308