Completed
Push — master ( 39aa91...270cc6 )
by Mahmoud
03:23
created

TestingTrait::getTestingImage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 4
1
<?php
2
3
namespace App\Port\Tests\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
     * This returned visitor is a normal user, with `visitor_id` means
137
     * before he became a registered user (can login) was a visitor.
138
     * So this can be used to test endpoints that are protected by visitors
139
     * access.
140
     *
141
     * @return  App\Containers\User\Models\User|mixed
142
     */
143
    public function getVisitor()
144
    {
145
        $user = $this->getLoggedInTestingUser();
146
147
        $user->visitor_id = str_random('20');
148
        unset($user->token);
149
        $user->save();
150
151
        return $user;
152
    }
153
154
    /**
155
     * @return  App\Containers\User\Models\User|mixed
156
     */
157
    public function getLoggedInTestingAdmin()
158
    {
159
        $user = $this->getLoggedInTestingUser();
160
161
        $user = $this->makeAdmin($user);
162
163
        return $user;
164
    }
165
166
    /**
167
     * @param $user
168
     *
169
     * @return  App\Containers\User\Models\User
170
     */
171
    public function makeAdmin(User $user)
172
    {
173
        $adminRole = Role::where('name', 'admin')->first();
174
175
        $user->assignRole($adminRole);
176
177
        return $user;
178
    }
179
180
    /**
181
     * get teh current logged in user token.
182
     *
183
     * @return string
184
     */
185
    public function getLoggedInTestingUserToken()
186
    {
187
        return $this->getLoggedInTestingUser()->token;
188
    }
189
190
    /**
191
     * @param null $userDetails
192
     *
193
     * @return  mixed
194
     */
195
    public function registerAndLoginTestingUser($userDetails = null)
196
    {
197
        // if no user detail provided, use the default details.
198
        if (!$userDetails) {
199
            $userDetails = [
200
                'name'     => 'Mahmoud Zalt',
201
                'email'    => '[email protected]',
202
                'password' => 'secret.Pass7',
203
            ];
204
        }
205
206
        $createUserAction = App::make(CreateUserAction::class);
207
208
        // 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...
209
        $user = $createUserAction->run(
210
            $userDetails['email'],
211
            $userDetails['password'],
212
            $userDetails['name'],
213
            null,
214
            null,
215
            true
216
        );
217
218
        return $this->loggedInTestingUser = $user;
219
    }
220
221
    /**
222
     * @param null $userDetails
223
     *
224
     * @return  mixed
225
     */
226
    public function registerAndLoginTestingAdmin($userDetails = null)
227
    {
228
        $user = $this->registerAndLoginTestingUser($userDetails);
229
230
        $user = $this->makeAdmin($user);
231
232
        return $user;
233
    }
234
235
    /**
236
     * Normal user with Developer Role
237
     *
238
     * @param null $userDetails
239
     *
240
     * @return  mixed
241
     */
242
    public function registerAndLoginTestingDeveloper($userDetails = null)
243
    {
244
        $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...
245
246
        // Give Developer Role to this User if he doesn't have it already
247
        if (!$user->hasRole('developer')) {
248
            App::make(AssignRoleTask::class)->run($user, ['developer']);
249
        }
250
251
        return $user;
252
    }
253
254
    /**
255
     * @param $keys
256
     * @param $response
257
     */
258
    public function assertResponseContainKeys($keys, $response)
259
    {
260
        if (!is_array($keys)) {
261
            $keys = (array)$keys;
262
        }
263
264
        foreach ($keys as $key) {
265
            $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...
266
        }
267
    }
268
269
    /**
270
     * @param $values
271
     * @param $response
272
     */
273
    public function assertResponseContainValues($values, $response)
274
    {
275
        if (!is_array($values)) {
276
            $values = (array)$values;
277
        }
278
279
        foreach ($values as $value) {
280
            $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...
281
        }
282
    }
283
284
    /**
285
     * @param $data
286
     * @param $response
287
     */
288
    public function assertResponseContainKeyValue($data, $response)
289
    {
290
        $response = json_encode(LaravelArr::sortRecursive(
291
            (array)$this->responseToArray($response)
292
        ));
293
294
        foreach (LaravelArr::sortRecursive($data) as $key => $value) {
295
            $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...
296
            $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...
297
                "The JSON fragment [ {$expected} ] does not exist in the response [ {$response} ].");
298
        }
299
    }
300
301
    /**
302
     * Migrate the database.
303
     */
304
    public function migrateDatabase()
305
    {
306
        Artisan::call('migrate');
307
    }
308
309
    /**
310
     * @param $response
311
     *
312
     * @return  mixed
313
     */
314
    private function responseToArray($response)
315
    {
316
        if ($response instanceof \Illuminate\Http\Response) {
317
            $response = json_decode($response->getContent(), true);
318
        }
319
320
        if (array_key_exists('data', $response)) {
321
            $response = $response['data'];
322
        }
323
324
        return $response;
325
    }
326
327
    /**
328
     * Format the given key and value into a JSON string for expectation checks.
329
     *
330
     * @param string $key
331
     * @param mixed  $value
332
     *
333
     * @return string
334
     */
335
    private function formatToKeyValueToString($key, $value)
336
    {
337
        $expected = json_encode([$key => $value]);
338
339
        if (LaravelStr::startsWith($expected, '{')) {
340
            $expected = substr($expected, 1);
341
        }
342
343
        if (LaravelStr::endsWith($expected, '}')) {
344
            $expected = substr($expected, 0, -1);
345
        }
346
347
        return $expected;
348
    }
349
350
    /**
351
     * Mocking helper
352
     *
353
     * @param $class
354
     *
355
     * @return  \Mockery\MockInterface
356
     */
357
    public function mock($class)
358
    {
359
        $mock = Mockery::mock($class);
360
        App::instance($class, $mock);
361
362
        return $mock;
363
    }
364
365
    /**
366
     * get response object, get the string content from it and convert it to an std object
367
     * making it easier to read
368
     *
369
     * @param $response
370
     *
371
     * @return  mixed
372
     */
373
    public function getResponseObject(Response $response)
374
    {
375
        return json_decode($response->getContent());
376
    }
377
378
    /**
379
     * Inject the ID in the Endpoint URI
380
     *
381
     * Example: you give it ('users/{id}/stores', 100) it returns 'users/100/stores'
382
     *
383
     * @param      $endpoint
384
     * @param      $id
385
     * @param bool $skipEncoding
386
     *
387
     * @return  mixed
388
     */
389
    public function injectEndpointId($endpoint, $id, $skipEncoding = false)
390
    {
391
        // In case Hash ID is enabled it will encode the ID first
392
        if (Config::get('hello.hash-id')) {
393
394
            if (!$skipEncoding) {
395
                $id = Hashids::encode($id);
396
            }
397
        }
398
399
        return str_replace("{id}", $id, $endpoint);
400
    }
401
402
    /**
403
     * Create Application in the database with Token based on the User who made the request.
404
     * And return headers array with the Application stored token in it.
405
     * This is made to be used with the endpoints protected with `app.auth` middleware.
406
     *
407
     * NOTE: make sure you call this function before getting the `logged in testing user` in your tests.
408
     * TODO: will fix this line above later.
409
     *
410
     * @param string $appName
411
     * @param null   $userDetails
412
     *
413
     * @return  mixed
414
     */
415
    public function getApplicationTokenHeader($appName = 'Testing App', $userDetails = null)
416
    {
417
        $user = $this->registerAndLoginTestingDeveloper($userDetails);
418
419
        $application = (App::make(CreateApplicationWithTokenAction::class))->run($appName, $user->id);
420
421
        $headers['Authorization'] = 'Bearer ' . $application->token;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$headers was never initialized. Although not strictly required by PHP, it is generally a good practice to add $headers = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
422
423
        return $headers;
424
    }
425
426
    /**
427
     * override default URL subDomain in case you want to change it for some tests
428
     *
429
     * @param      $subDomain
430
     * @param null $url
431
     */
432
    public function overrideSubDomain($subDomain, $url = null)
433
    {
434
        $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...
435
436
        $info = parse_url($url);
437
438
        $array = explode('.', $info['host']);
439
440
        $withoutDomain = (array_key_exists(count($array) - 2,
441
                $array) ? $array[count($array) - 2] : '') . '.' . $array[count($array) - 1];
442
443
        $newSubDomain = $info['scheme'] . '://' . $subDomain . '.' . $withoutDomain;
444
445
        $this->baseUrl = $newSubDomain;
446
    }
447
448
}
449