Completed
Push — master ( 4a0591...b2a3a3 )
by Mahmoud
11:29
created

TestingTrait::apiCall()   C

Complexity

Conditions 11
Paths 28

Size

Total Lines 41
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 1 Features 1
Metric Value
c 6
b 1
f 1
dl 0
loc 41
rs 5.2653
cc 11
eloc 25
nc 28
nop 5

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace App\Port\Tests\PHPUnit\Traits;
4
5
use App;
6
use App\Containers\User\Actions\RegisterUserAction;
7
use Dingo\Api\Http\Response as DingoAPIResponse;
8
use Illuminate\Http\Response;
9
use Illuminate\Support\Arr as LaravelArr;
10
use Illuminate\Support\Str as LaravelStr;
11
use Mockery;
12
use Symfony\Component\Debug\Exception\UndefinedMethodException;
13
14
/**
15
 * Class TestingTrait.
16
 *
17
 * All the functions in this trait are accessible from all your tests.
18
 *
19
 * @author  Mahmoud Zalt <[email protected]>
20
 */
21
trait TestingTrait
22
{
23
24
    /**
25
     * the Logged in user, used for protected routes.
26
     *
27
     * @var User
28
     */
29
    public $loggedInTestingUser;
30
31
    /**
32
     * @param           $endpoint
33
     * @param string    $verb
34
     * @param array     $data
35
     * @param bool|true $protected
36
     * @param array     $header
37
     *
38
     * @throws \Symfony\Component\Debug\Exception\UndefinedMethodException
39
     *
40
     * @return mixed
41
     */
42
    public function apiCall($endpoint, $verb = 'get', array $data = [], $protected = true, array $header = [])
43
    {
44
        $content = json_encode($data);
45
46
        $headers = array_merge([
47
            'CONTENT_LENGTH' => mb_strlen($content, '8bit'),
48
//            'CONTENT_TYPE' => 'application/json',
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
49
            'Accept'         => 'application/json',
50
        ], $header);
51
52
53
        // if endpoint is protected (requires token to access it's functionality)
54
        if ($protected && !array_has($header, 'Authorization')) {
55
            // append the token to the header
56
            $headers['Authorization'] = 'Bearer ' . $this->getLoggedInTestingUserToken();
57
        }
58
59
        if (!$protected && !array_has($header, 'Visitor-Id')) {
60
            // append the Device ID to the header (IPhone UUID, Android ID, ...)
61
            $headers['Visitor-Id'] = str_random(40);
62
        }
63
64
        $verb = strtolower($verb);
65
66
        switch ($verb) {
67
            case 'get':
68
                $endpoint = $data ? $endpoint . '?' . http_build_query($data) : $endpoint;
69
                $response = $this->get($endpoint, $headers)->response;
0 ignored issues
show
Bug introduced by
It seems like get() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
70
                break;
71
            case 'post':
72
            case 'put':
73
            case 'patch':
74
            case 'delete':
75
                $response = $this->{$verb}($endpoint, $data, $headers)->response;
76
                break;
77
            default:
78
                throw new UndefinedMethodException('Undefined HTTP Verb (' . $verb . ').');
0 ignored issues
show
Bug introduced by
The call to UndefinedMethodException::__construct() misses a required argument $previous.

This check looks for function calls that miss required arguments.

Loading history...
79
        }
80
81
        return $response;
82
    }
83
84
    /**
85
     * @param \Dingo\Api\Http\Response $response
86
     * @param array                    $messages
87
     */
88
    public function assertValidationErrorContain(DingoAPIResponse $response, array $messages)
89
    {
90
91
        $arrayResponse = json_decode($response->getContent());
92
93
        foreach ($messages as $key => $value) {
94
            $this->assertEquals($arrayResponse->errors->{$key}[0], $value);
0 ignored issues
show
Bug introduced by
It seems like assertEquals() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
95
        }
96
    }
97
98
    /**
99
     * get teh current logged in user.
100
     *
101
     * @return \App\Port\Tests\PHPUnit\Traits\User|mixed
102
     */
103
    public function getLoggedInTestingUser()
104
    {
105
        $user = $this->loggedInTestingUser;
106
107
        if (!$user) {
108
            $user = $this->registerAndLoginTestingUser();
109
        }
110
111
        return $user;
112
    }
113
114
    /**
115
     * get teh current logged in user token.
116
     *
117
     * @return string
118
     */
119
    public function getLoggedInTestingUserToken()
120
    {
121
        return $this->getLoggedInTestingUser()->token;
122
    }
123
124
    /**
125
     * @param null $userDetails
126
     *
127
     * @return mixed
128
     */
129
    public function registerAndLoginTestingUser($userDetails = null)
130
    {
131
        // if no user detail provided, use the default details.
132
        if (!$userDetails) {
133
            $userDetails = [
134
                'name'     => 'Mahmoud Zalt',
135
                'email'    => '[email protected]',
136
                'password' => 'secret.Pass7',
137
            ];
138
        }
139
140
        $RegisterUserAction = App::make(RegisterUserAction::class);
141
142
        // create new user and login (true)
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
143
        $user = $RegisterUserAction->run(
144
            $userDetails['email'],
145
            $userDetails['password'],
146
            $userDetails['name'],
147
            true
148
        );
149
150
        return $this->loggedInTestingUser = $user;
151
    }
152
153
    /**
154
     * @param $keys
155
     * @param $response
156
     */
157
    public function assertResponseContainKeys($keys, $response)
158
    {
159
        if (!is_array($keys)) {
160
            $keys = (array)$keys;
161
        }
162
163
        foreach ($keys as $key) {
164
            $this->assertTrue(array_key_exists($key, $this->responseToArray($response)));
0 ignored issues
show
Bug introduced by
It seems like assertTrue() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
165
        }
166
    }
167
168
    /**
169
     * @param $values
170
     * @param $response
171
     */
172
    public function assertResponseContainValues($values, $response)
173
    {
174
        if (!is_array($values)) {
175
            $values = (array)$values;
176
        }
177
178
        foreach ($values as $value) {
179
            $this->assertTrue(in_array($value, $this->responseToArray($response)));
0 ignored issues
show
Bug introduced by
It seems like assertTrue() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
180
        }
181
    }
182
183
    /**
184
     * @param $data
185
     * @param $response
186
     */
187
    public function assertResponseContainKeyValue($data, $response)
188
    {
189
        $response = json_encode(LaravelArr::sortRecursive(
190
            (array)$this->responseToArray($response)
191
        ));
192
193
        foreach (LaravelArr::sortRecursive($data) as $key => $value) {
194
            $expected = $this->formatToExpectedJson($key, $value);
0 ignored issues
show
Bug introduced by
It seems like formatToExpectedJson() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
195
            $this->assertTrue(LaravelStr::contains($response, $expected),
0 ignored issues
show
Bug introduced by
It seems like assertTrue() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
196
                "The JSON fragment [ {$expected} ] does not exist in the response [ {$response} ].");
197
        }
198
    }
199
200
    /**
201
     * Prettify printing JSON data (mainly for API responses) in the terminal.
202
     *
203
     * @param $json
204
     *
205
     * @return string
206
     */
207
    public function ddj($json)
208
    {
209
        $result = '';
210
        $pos = 0;
211
        $strLen = strlen($json);
212
        $indentStr = '  ';
213
        $newLine = "\n";
214
        $prevChar = '';
215
        $outOfQuotes = true;
216
217
        for ($i = 0; $i <= $strLen; ++$i) {
218
219
            // Grab the next character in the string.
220
            $char = substr($json, $i, 1);
221
222
            // Are we inside a quoted string?
223
            if ($char == '"' && $prevChar != '\\') {
224
                $outOfQuotes = !$outOfQuotes;
225
226
                // If this character is the end of an element,
227
                // output a new line and indent the next line.
228
            } else {
229
                if (($char == '}' || $char == ']') && $outOfQuotes) {
230
                    $result .= $newLine;
231
                    --$pos;
232
                    for ($j = 0; $j < $pos; ++$j) {
233
                        $result .= $indentStr;
234
                    }
235
                }
236
            }
237
238
            // Add the character to the result string.
239
            $result .= $char;
240
241
            // If the last character was the beginning of an element,
242
            // output a new line and indent the next line.
243
            if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) {
244
                $result .= $newLine;
245
                if ($char == '{' || $char == '[') {
246
                    ++$pos;
247
                }
248
249
                for ($j = 0; $j < $pos; ++$j) {
250
                    $result .= $indentStr;
251
                }
252
            }
253
254
            $prevChar = $char;
255
        }
256
257
        dd($result);
258
    }
259
260
    /**
261
     * Migrate the database.
262
     */
263
    public function migrateDatabase()
264
    {
265
        \Artisan::call('migrate');
266
    }
267
268
    /**
269
     * @param $response
270
     *
271
     * @return mixed
272
     */
273
    private function responseToArray($response)
274
    {
275
        if ($response instanceof \Illuminate\Http\Response) {
276
            $response = json_decode($response->getContent(), true);
277
        }
278
279
        if (array_key_exists('data', $response)) {
280
            $response = $response['data'];
281
        }
282
283
        return $response;
284
    }
285
286
    /**
287
     * Format the given key and value into a JSON string for expectation checks.
288
     *
289
     * @param string $key
290
     * @param mixed  $value
291
     *
292
     * @return string
293
     */
294
    private function formatToKeyValueToString($key, $value)
295
    {
296
        $expected = json_encode([$key => $value]);
297
298
        if (LaravelStr::startsWith($expected, '{')) {
299
            $expected = substr($expected, 1);
300
        }
301
302
        if (LaravelStr::endsWith($expected, '}')) {
303
            $expected = substr($expected, 0, -1);
304
        }
305
306
        return $expected;
307
    }
308
309
    /**
310
     * Mocking helper
311
     *
312
     * @param $class
313
     *
314
     * @return  \Mockery\MockInterface
315
     */
316
    public function mock($class)
317
    {
318
        $mock = Mockery::mock($class);
319
        $this->app->instance($class, $mock);
0 ignored issues
show
Bug introduced by
The property app does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
320
321
        return $mock;
322
    }
323
324
    /**
325
     * get response object, get the string content from it and convert it to an std object
326
     * making it easier to read
327
     *
328
     * @param $response
329
     *
330
     * @return  mixed
331
     */
332
    public function getResponseObject(Response $response)
333
    {
334
        return json_decode($response->getContent());
335
    }
336
337
}
338