Completed
Push — master ( 91dac3...ff73e1 )
by Mahmoud
04:27
created

TestingTrait::setupTestingUserAccess()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 7
nc 6
nop 2
1
<?php
2
3
namespace App\Port\Test\PHPUnit\Traits;
4
5
use App;
6
use App\Containers\Authorization\Tasks\AssignRoleTask;
7
use App\Containers\User\Actions\CreateUserAction;
8
use App\Containers\User\Models\User;
9
use Artisan;
10
use Dingo\Api\Http\Response as DingoAPIResponse;
11
use Illuminate\Http\Response;
12
use Illuminate\Http\UploadedFile;
13
use Illuminate\Support\Arr as LaravelArr;
14
use Illuminate\Support\Facades\Config;
15
use Illuminate\Support\Str as LaravelStr;
16
use Mockery;
17
use Symfony\Component\Debug\Exception\UndefinedMethodException;
18
use Vinkla\Hashids\Facades\Hashids;
19
20
/**
21
 * Class TestingTrait.
22
 *
23
 * All the functions in this trait are accessible from all your tests.
24
 *
25
 * @author  Mahmoud Zalt <[email protected]>
26
 */
27
trait TestingTrait
28
{
29
30
    /**
31
     * the Logged in user, used for protected routes.
32
     *
33
     * @var User
34
     */
35
    public $loggedInTestingUser;
36
37
    /**
38
     * @param        $endpoint
39
     * @param string $verb
40
     * @param array  $data
41
     * @param bool   $protected
42
     * @param array  $headers
43
     *
44
     * @return  mixed
45
     * @throws \Symfony\Component\Debug\Exception\UndefinedMethodException
46
     */
47
    public function apiCall($endpoint, $verb = 'get', array $data = [], $protected = true, array $headers = [])
48
    {
49
        // if endpoint is protected (requires token to access it's functionality)
50
        if ($protected && !array_has($headers, 'Authorization')) {
51
            // append the token to the header
52
            $headers['Authorization'] = 'Bearer ' . $this->getLoggedInTestingUserToken();
53
        }
54
55
        switch ($verb) {
56
            case 'get':
57
                $endpoint = $data ? $endpoint . '?' . http_build_query($data) : $endpoint;
58
                $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...
59
                break;
60
            case 'post':
61
            case 'put':
62
            case 'patch':
63
            case 'delete':
64
                $response = $this->{$verb}($endpoint, $data, $headers)->response;
65
                break;
66
            case 'json:post':
67
                $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...
68
                break;
69
            default:
70
                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...
71
        }
72
73
        return $response;
74
    }
75
76
    /**
77
     * @param        $fileName
78
     * @param        $stubDirPath
79
     * @param string $mimeType
80
     * @param null   $size
81
     *
82
     * @return  \Illuminate\Http\UploadedFile
83
     */
84
    public function getTestingFile($fileName, $stubDirPath, $mimeType = 'text/plain', $size = null)
85
    {
86
        $file = $stubDirPath . $fileName;
87
88
        return new UploadedFile($file, $fileName, $mimeType, $size, null, true); // null = null | $testMode = 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...
89
    }
90
91
    /**
92
     * @param        $imageName
93
     * @param        $stubDirPath
94
     * @param string $mimeType
95
     * @param null   $size
96
     *
97
     * @return  \Illuminate\Http\UploadedFile
98
     */
99
    public function getTestingImage($imageName, $stubDirPath, $mimeType = 'image/jpeg', $size = null)
100
    {
101
        return $this->getTestingFile($imageName, $stubDirPath, $mimeType, $size);
102
    }
103
104
    /**
105
     * @param \Dingo\Api\Http\Response $response
106
     * @param array                    $messages
107
     */
108
    public function assertValidationErrorContain(DingoAPIResponse $response, array $messages)
109
    {
110
        $arrayResponse = json_decode($response->getContent());
111
112
        foreach ($messages as $key => $value) {
113
            $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...
114
        }
115
    }
116
117
    /**
118
     * get teh current logged in user OR create new one if no one exist
119
     *
120
     * @param null $access
121
     *
122
     * @return  \App\Containers\User\Models\User|mixed
123
     */
124
    public function getTestingUser($access = null)
125
    {
126
        if (!$user = $this->loggedInTestingUser) {
127
            $user = $this->createTestingUser($access);
128
        }
129
130
        return $user;
131
    }
132
133
    /**
134
     * @param null $permissions
135
     *
136
     * @return  \App\Containers\User\Models\User|mixed
137
     */
138
    public function getTestingAdmin($permissions = null)
139
    {
140
        return $this->getTestingUser([
141
            'roles'        => 'admin',
142
            '$permissions' => $permissions,
143
        ]);
144
    }
145
146
    /**
147
     * get teh current logged in user token.
148
     *
149
     * @return string
150
     */
151
    public function getLoggedInTestingUserToken()
152
    {
153
        return $this->getTestingUser()->token;
154
    }
155
156
    /**
157
     * @param null $access
158
     * @param null $userDetails
159
     *
160
     * @return  mixed
161
     */
162
    public function createTestingUser($access = null, $userDetails = null)
163
    {
164
        // if no user detail provided, use the default details.
165
        $userDetails = $userDetails ? : [
166
            'name'     => 'Mahmoud Zalt',
167
            'email'    => '[email protected]',
168
            'password' => 'secret.Pass7',
169
        ];
170
171
        // 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...
172
        $user = App::make(CreateUserAction::class)->run($userDetails['email'], $userDetails['password'],
173
            $userDetails['name'], null, null, true
174
        );
175
176
        $user = $this->setupTestingUserAccess($user, $access ? : (isset($this->access) ? $this->access : null));
0 ignored issues
show
Bug introduced by
The property access 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...
177
178
        return $this->loggedInTestingUser = $user;
179
    }
180
181
    /**
182
     * @param $user
183
     * @param $access
184
     *
185
     * @return  mixed
186
     */
187
    private function setupTestingUserAccess($user, $access)
188
    {
189
        if (isset($access['permissions']) && !empty($access['permissions'])) {
190
            $user->givePermissionTo($access['permissions']);
191
        }
192
        if (isset($access['roles']) && !empty($access['roles'])) {
193
            if (!$user->hasRole($access['roles'])) {
194
                $user->assignRole($access['roles']);
195
            }
196
        }
197
198
        return $user;
199
    }
200
201
    /**
202
     * @param null $userDetails
203
     *
204
     * @return  mixed
205
     */
206
    public function registerAndLoginTestingAdmin($userDetails = null)
207
    {
208
        $user = $this->createTestingUser($userDetails);
209
210
        $user = $this->makeAdmin($user);
0 ignored issues
show
Bug introduced by
It seems like makeAdmin() 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...
211
212
        return $user;
213
    }
214
215
    /**
216
     * Normal user with Developer Role
217
     *
218
     * @param null $userDetails
219
     *
220
     * @return  \App\Containers\User\Models\User|mixed
221
     */
222
    public function registerAndLoginTestingDeveloper($userDetails = null)
223
    {
224
        $user = $this->getTestingUser(null, $userDetails);
0 ignored issues
show
Unused Code introduced by
The call to TestingTrait::getTestingUser() 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...
225
226
        // Give Developer Role to this User if he doesn't have it already
227
        if (!$user->hasRole('developer')) {
228
            App::make(AssignRoleTask::class)->run($user, ['developer']);
229
        }
230
231
        return $user;
232
    }
233
234
    /**
235
     * @param $keys
236
     * @param $response
237
     */
238
    public function assertResponseContainKeys($keys, $response)
239
    {
240
        if (!is_array($keys)) {
241
            $keys = (array)$keys;
242
        }
243
244
        foreach ($keys as $key) {
245
            $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...
246
        }
247
    }
248
249
    /**
250
     * @param $values
251
     * @param $response
252
     */
253
    public function assertResponseContainValues($values, $response)
254
    {
255
        if (!is_array($values)) {
256
            $values = (array)$values;
257
        }
258
259
        foreach ($values as $value) {
260
            $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...
261
        }
262
    }
263
264
    /**
265
     * @param $data
266
     * @param $response
267
     */
268
    public function assertResponseContainKeyValue($data, $response)
269
    {
270
        $response = json_encode(LaravelArr::sortRecursive(
271
            (array)$this->responseToArray($response)
272
        ));
273
274
        foreach (LaravelArr::sortRecursive($data) as $key => $value) {
275
            $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...
276
            $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...
277
                "The JSON fragment [ {$expected} ] does not exist in the response [ {$response} ].");
278
        }
279
    }
280
281
    /**
282
     * Migrate the database.
283
     */
284
    public function migrateDatabase()
285
    {
286
        Artisan::call('migrate');
287
    }
288
289
    /**
290
     * @param $response
291
     *
292
     * @return  mixed
293
     */
294
    private function responseToArray($response)
295
    {
296
        if ($response instanceof \Illuminate\Http\Response) {
297
            $response = json_decode($response->getContent(), true);
298
        }
299
300
        if (array_key_exists('data', $response)) {
301
            $response = $response['data'];
302
        }
303
304
        return $response;
305
    }
306
307
    /**
308
     * Format the given key and value into a JSON string for expectation checks.
309
     *
310
     * @param string $key
311
     * @param mixed  $value
312
     *
313
     * @return string
314
     */
315
    private function formatToKeyValueToString($key, $value)
316
    {
317
        $expected = json_encode([$key => $value]);
318
319
        if (LaravelStr::startsWith($expected, '{')) {
320
            $expected = substr($expected, 1);
321
        }
322
323
        if (LaravelStr::endsWith($expected, '}')) {
324
            $expected = substr($expected, 0, -1);
325
        }
326
327
        return $expected;
328
    }
329
330
    /**
331
     * Mocking helper
332
     *
333
     * @param $class
334
     *
335
     * @return  \Mockery\MockInterface
336
     */
337
    public function mock($class)
338
    {
339
        $mock = Mockery::mock($class);
340
        App::instance($class, $mock);
341
342
        return $mock;
343
    }
344
345
    /**
346
     * get response object, get the string content from it and convert it to an std object
347
     * making it easier to read
348
     *
349
     * @param $response
350
     *
351
     * @return  mixed
352
     */
353
    public function getResponseObject(Response $response)
354
    {
355
        return json_decode($response->getContent());
356
    }
357
358
    /**
359
     * Inject the ID in the Endpoint URI
360
     *
361
     * Example: you give it ('users/{id}/stores', 100) it returns 'users/100/stores'
362
     *
363
     * @param      $endpoint
364
     * @param      $id
365
     * @param bool $skipEncoding
366
     *
367
     * @return  mixed
368
     */
369
    public function injectEndpointId($endpoint, $id, $skipEncoding = false)
370
    {
371
        // In case Hash ID is enabled it will encode the ID first
372
        if (Config::get('hello.hash-id')) {
373
374
            if (!$skipEncoding) {
375
                $id = Hashids::encode($id);
376
            }
377
        }
378
379
        return str_replace("{id}", $id, $endpoint);
380
    }
381
382
    /**
383
     * override default URL subDomain in case you want to change it for some tests
384
     *
385
     * @param      $subDomain
386
     * @param null $url
387
     */
388
    public function overrideSubDomain($subDomain, $url = null)
389
    {
390
        $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...
391
392
        $info = parse_url($url);
393
394
        $array = explode('.', $info['host']);
395
396
        $withoutDomain = (array_key_exists(count($array) - 2,
397
                $array) ? $array[count($array) - 2] : '') . '.' . $array[count($array) - 1];
398
399
        $newSubDomain = $info['scheme'] . '://' . $subDomain . '.' . $withoutDomain;
400
401
        $this->baseUrl = $newSubDomain;
402
    }
403
404
}
405