Completed
Push — master ( 5c46bf...f77784 )
by Mahmoud
03:22
created

TestingTrait::apiCall()   D

Complexity

Conditions 10
Paths 16

Size

Total Lines 28
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 20
nc 16
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\Test\PHPUnit\Traits;
4
5
use App;
6
use App\Containers\Application\Actions\CreateApplicationWithTokenAction;
7
use App\Containers\Authorization\Models\Role;
8
use App\Containers\Authorization\Tasks\AssignRoleTask;
9
use App\Containers\User\Actions\CreateUserAction;
10
use App\Containers\User\Models\User;
11
use Artisan;
12
use Dingo\Api\Http\Response as DingoAPIResponse;
13
use Illuminate\Http\Response;
14
use Illuminate\Http\UploadedFile;
15
use Illuminate\Support\Arr as LaravelArr;
16
use Illuminate\Support\Facades\Config;
17
use Illuminate\Support\Str as LaravelStr;
18
use Mockery;
19
use Symfony\Component\Debug\Exception\UndefinedMethodException;
20
use Vinkla\Hashids\Facades\Hashids;
21
22
/**
23
 * Class TestingTrait.
24
 *
25
 * All the functions in this trait are accessible from all your tests.
26
 *
27
 * @author  Mahmoud Zalt <[email protected]>
28
 */
29
trait TestingTrait
30
{
31
32
    /**
33
     * the Logged in user, used for protected routes.
34
     *
35
     * @var User
36
     */
37
    public $loggedInTestingUser;
38
39
    /**
40
     * @param        $endpoint
41
     * @param string $verb
42
     * @param array  $data
43
     * @param bool   $protected
44
     * @param array  $headers
45
     *
46
     * @return  mixed
47
     * @throws \Symfony\Component\Debug\Exception\UndefinedMethodException
48
     */
49
    public function apiCall($endpoint, $verb = 'get', array $data = [], $protected = true, array $headers = [])
50
    {
51
        // if endpoint is protected (requires token to access it's functionality)
52
        if ($protected && !array_has($headers, 'Authorization')) {
53
            // append the token to the header
54
            $headers['Authorization'] = 'Bearer ' . $this->getLoggedInTestingUserToken();
55
        }
56
57
        switch ($verb) {
58
            case 'get':
59
                $endpoint = $data ? $endpoint . '?' . http_build_query($data) : $endpoint;
60
                $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...
61
                break;
62
            case 'post':
63
            case 'put':
64
            case 'patch':
65
            case 'delete':
66
                $response = $this->{$verb}($endpoint, $data, $headers)->response;
67
                break;
68
            case 'json:post':
69
                $response = $this->json('post', $endpoint, $data, $headers)->response;
0 ignored issues
show
Bug introduced by
It seems like json() 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
            default:
72
                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...
73
        }
74
75
        return $response;
76
    }
77
78
    /**
79
     * @param        $fileName
80
     * @param        $stubDirPath
81
     * @param string $mimeType
82
     * @param null   $size
83
     *
84
     * @return  \Illuminate\Http\UploadedFile
85
     */
86
    public function getTestingFile($fileName, $stubDirPath, $mimeType = 'text/plain', $size = null)
87
    {
88
        $file = $stubDirPath . $fileName;
89
90
        return new UploadedFile($file, $fileName, $mimeType, $size, $error = null, $testMode = true);
91
    }
92
93
    /**
94
     * @param        $imageName
95
     * @param        $stubDirPath
96
     * @param string $mimeType
97
     * @param null   $size
98
     *
99
     * @return  \Illuminate\Http\UploadedFile
100
     */
101
    public function getTestingImage($imageName, $stubDirPath, $mimeType = 'image/jpeg', $size = null)
102
    {
103
        return $this->getTestingFile($imageName, $stubDirPath, $mimeType, $size);
104
    }
105
106
    /**
107
     * @param \Dingo\Api\Http\Response $response
108
     * @param array                    $messages
109
     */
110
    public function assertValidationErrorContain(DingoAPIResponse $response, array $messages)
111
    {
112
        $arrayResponse = json_decode($response->getContent());
113
114
        foreach ($messages as $key => $value) {
115
            $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...
116
        }
117
    }
118
119
    /**
120
     * get teh current logged in user.
121
     *
122
     * @return App\Containers\User\Models\User
123
     */
124
    public function getLoggedInTestingUser()
125
    {
126
        $user = $this->loggedInTestingUser;
127
128
        if (!$user) {
129
            $user = $this->registerAndLoginTestingUser();
130
        }
131
132
        return $user;
133
    }
134
135
    /**
136
     * @return  App\Containers\User\Models\User|mixed
137
     */
138
    public function getLoggedInTestingAdmin()
139
    {
140
        $user = $this->getLoggedInTestingUser();
141
142
        $user = $this->makeAdmin($user);
143
144
        return $user;
145
    }
146
147
    /**
148
     * @param $user
149
     *
150
     * @return  App\Containers\User\Models\User
151
     */
152
    public function makeAdmin(User $user)
153
    {
154
        $adminRole = Role::where('name', 'admin')->first();
155
156
        $user->assignRole($adminRole);
157
158
        return $user;
159
    }
160
161
    /**
162
     * get teh current logged in user token.
163
     *
164
     * @return string
165
     */
166
    public function getLoggedInTestingUserToken()
167
    {
168
        return $this->getLoggedInTestingUser()->token;
169
    }
170
171
    /**
172
     * @param null $userDetails
173
     *
174
     * @return  mixed
175
     */
176
    public function registerAndLoginTestingUser($userDetails = null)
177
    {
178
        // if no user detail provided, use the default details.
179
        if (!$userDetails) {
180
            $userDetails = [
181
                'name'     => 'Mahmoud Zalt',
182
                'email'    => '[email protected]',
183
                'password' => 'secret.Pass7',
184
            ];
185
        }
186
187
        $createUserAction = App::make(CreateUserAction::class);
188
189
        // 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...
190
        $user = $createUserAction->run(
191
            $userDetails['email'],
192
            $userDetails['password'],
193
            $userDetails['name'],
194
            null,
195
            null,
196
            true
197
        );
198
199
        return $this->loggedInTestingUser = $user;
200
    }
201
202
    /**
203
     * @param null $userDetails
204
     *
205
     * @return  mixed
206
     */
207
    public function registerAndLoginTestingAdmin($userDetails = null)
208
    {
209
        $user = $this->registerAndLoginTestingUser($userDetails);
210
211
        $user = $this->makeAdmin($user);
212
213
        return $user;
214
    }
215
216
    /**
217
     * Normal user with Developer Role
218
     *
219
     * @param null $userDetails
220
     *
221
     * @return  mixed
222
     */
223
    public function registerAndLoginTestingDeveloper($userDetails = null)
224
    {
225
        $user = $this->getLoggedInTestingUser($userDetails);
0 ignored issues
show
Unused Code introduced by
The call to TestingTrait::getLoggedInTestingUser() has too many arguments starting with $userDetails.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
226
227
        // Give Developer Role to this User if he doesn't have it already
228
        if (!$user->hasRole('developer')) {
229
            App::make(AssignRoleTask::class)->run($user, ['developer']);
230
        }
231
232
        return $user;
233
    }
234
235
    /**
236
     * @param $keys
237
     * @param $response
238
     */
239
    public function assertResponseContainKeys($keys, $response)
240
    {
241
        if (!is_array($keys)) {
242
            $keys = (array)$keys;
243
        }
244
245
        foreach ($keys as $key) {
246
            $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...
247
        }
248
    }
249
250
    /**
251
     * @param $values
252
     * @param $response
253
     */
254
    public function assertResponseContainValues($values, $response)
255
    {
256
        if (!is_array($values)) {
257
            $values = (array)$values;
258
        }
259
260
        foreach ($values as $value) {
261
            $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...
262
        }
263
    }
264
265
    /**
266
     * @param $data
267
     * @param $response
268
     */
269
    public function assertResponseContainKeyValue($data, $response)
270
    {
271
        $response = json_encode(LaravelArr::sortRecursive(
272
            (array)$this->responseToArray($response)
273
        ));
274
275
        foreach (LaravelArr::sortRecursive($data) as $key => $value) {
276
            $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...
277
            $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...
278
                "The JSON fragment [ {$expected} ] does not exist in the response [ {$response} ].");
279
        }
280
    }
281
282
    /**
283
     * Migrate the database.
284
     */
285
    public function migrateDatabase()
286
    {
287
        Artisan::call('migrate');
288
    }
289
290
    /**
291
     * @param $response
292
     *
293
     * @return  mixed
294
     */
295
    private function responseToArray($response)
296
    {
297
        if ($response instanceof \Illuminate\Http\Response) {
298
            $response = json_decode($response->getContent(), true);
299
        }
300
301
        if (array_key_exists('data', $response)) {
302
            $response = $response['data'];
303
        }
304
305
        return $response;
306
    }
307
308
    /**
309
     * Format the given key and value into a JSON string for expectation checks.
310
     *
311
     * @param string $key
312
     * @param mixed  $value
313
     *
314
     * @return string
315
     */
316
    private function formatToKeyValueToString($key, $value)
317
    {
318
        $expected = json_encode([$key => $value]);
319
320
        if (LaravelStr::startsWith($expected, '{')) {
321
            $expected = substr($expected, 1);
322
        }
323
324
        if (LaravelStr::endsWith($expected, '}')) {
325
            $expected = substr($expected, 0, -1);
326
        }
327
328
        return $expected;
329
    }
330
331
    /**
332
     * Mocking helper
333
     *
334
     * @param $class
335
     *
336
     * @return  \Mockery\MockInterface
337
     */
338
    public function mock($class)
339
    {
340
        $mock = Mockery::mock($class);
341
        App::instance($class, $mock);
342
343
        return $mock;
344
    }
345
346
    /**
347
     * get response object, get the string content from it and convert it to an std object
348
     * making it easier to read
349
     *
350
     * @param $response
351
     *
352
     * @return  mixed
353
     */
354
    public function getResponseObject(Response $response)
355
    {
356
        return json_decode($response->getContent());
357
    }
358
359
    /**
360
     * Inject the ID in the Endpoint URI
361
     *
362
     * Example: you give it ('users/{id}/stores', 100) it returns 'users/100/stores'
363
     *
364
     * @param      $endpoint
365
     * @param      $id
366
     * @param bool $skipEncoding
367
     *
368
     * @return  mixed
369
     */
370
    public function injectEndpointId($endpoint, $id, $skipEncoding = false)
371
    {
372
        // In case Hash ID is enabled it will encode the ID first
373
        if (Config::get('hello.hash-id')) {
374
375
            if (!$skipEncoding) {
376
                $id = Hashids::encode($id);
377
            }
378
        }
379
380
        return str_replace("{id}", $id, $endpoint);
381
    }
382
383
    /**
384
     * override default URL subDomain in case you want to change it for some tests
385
     *
386
     * @param      $subDomain
387
     * @param null $url
388
     */
389
    public function overrideSubDomain($subDomain, $url = null)
390
    {
391
        $url = ($url) ? : $this->baseUrl;
0 ignored issues
show
Bug introduced by
The property baseUrl 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...
392
393
        $info = parse_url($url);
394
395
        $array = explode('.', $info['host']);
396
397
        $withoutDomain = (array_key_exists(count($array) - 2,
398
                $array) ? $array[count($array) - 2] : '') . '.' . $array[count($array) - 1];
399
400
        $newSubDomain = $info['scheme'] . '://' . $subDomain . '.' . $withoutDomain;
401
402
        $this->baseUrl = $newSubDomain;
403
    }
404
405
}
406