Completed
Push — master ( f1dd30...727f83 )
by Mahmoud
04:46
created

TestingTrait::migrateDatabase()   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 0
1
<?php
2
3
namespace App\Port\Tests\PHPUnit\Traits;
4
5
use App;
6
use App\Containers\Authorization\Models\Role;
7
use App\Containers\User\Actions\CreateUserAction;
8
use Dingo\Api\Http\Response as DingoAPIResponse;
9
use Illuminate\Http\Response;
10
use Illuminate\Support\Arr as LaravelArr;
11
use Illuminate\Support\Str as LaravelStr;
12
use Mockery;
13
use Symfony\Component\Debug\Exception\UndefinedMethodException;
14
15
/**
16
 * Class TestingTrait.
17
 *
18
 * All the functions in this trait are accessible from all your tests.
19
 *
20
 * @author  Mahmoud Zalt <[email protected]>
21
 */
22
trait TestingTrait
23
{
24
25
    /**
26
     * the Logged in user, used for protected routes.
27
     *
28
     * @var User
29
     */
30
    public $loggedInTestingUser;
31
32
    /**
33
     * @param           $endpoint
34
     * @param string    $verb
35
     * @param array     $data
36
     * @param bool|true $protected
37
     * @param array     $header
38
     *
39
     * @throws \Symfony\Component\Debug\Exception\UndefinedMethodException
40
     *
41
     * @return mixed
42
     */
43
    public function apiCall($endpoint, $verb = 'get', array $data = [], $protected = true, array $header = [])
44
    {
45
        $content = json_encode($data);
46
47
        $headers = array_merge([
48
            'CONTENT_LENGTH' => mb_strlen($content, '8bit'),
49
//            '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...
50
            'Accept'         => 'application/json',
51
        ], $header);
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
     * @return  \App\Port\Tests\PHPUnit\Traits\User|mixed
116
     */
117
    public function getLoggedInTestingAdmin()
118
    {
119
        $user = $this->getLoggedInTestingUser();
120
121
        $user = $this->makeAdmin($user);
122
123
        return $user;
124
    }
125
126
    /**
127
     * @param $user
128
     *
129
     * @return  mixed
130
     */
131
    public function makeAdmin($user)
132
    {
133
        $adminRole = Role::where('name', 'admin')->first();
134
135
        $user->attachRole($adminRole);
136
137
        return $user;
138
    }
139
140
    /**
141
     * get teh current logged in user token.
142
     *
143
     * @return string
144
     */
145
    public function getLoggedInTestingUserToken()
146
    {
147
        return $this->getLoggedInTestingUser()->token;
148
    }
149
150
    /**
151
     * @param null $userDetails
152
     *
153
     * @return mixed
154
     */
155
    public function registerAndLoginTestingUser($userDetails = null)
156
    {
157
        // if no user detail provided, use the default details.
158
        if (!$userDetails) {
159
            $userDetails = [
160
                'name'     => 'Mahmoud Zalt',
161
                'email'    => '[email protected]',
162
                'password' => 'secret.Pass7',
163
            ];
164
        }
165
166
        $CreateUserAction = App::make(CreateUserAction::class);
167
168
        // 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...
169
        $user = $CreateUserAction->run(
170
            $userDetails['email'],
171
            $userDetails['password'],
172
            $userDetails['name'],
173
            true
174
        );
175
176
        return $this->loggedInTestingUser = $user;
177
    }
178
179
    /**
180
     * @param null $userDetails
181
     *
182
     * @return  mixed
183
     */
184
    public function registerAndLoginTestingAdmin($userDetails = null)
185
    {
186
        $user = $this->registerAndLoginTestingUser($userDetails);
187
188
        $user = $this->makeAdmin($user);
189
190
        return $user;
191
    }
192
193
    /**
194
     * @param $keys
195
     * @param $response
196
     */
197
    public function assertResponseContainKeys($keys, $response)
198
    {
199
        if (!is_array($keys)) {
200
            $keys = (array)$keys;
201
        }
202
203
        foreach ($keys as $key) {
204
            $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...
205
        }
206
    }
207
208
    /**
209
     * @param $values
210
     * @param $response
211
     */
212
    public function assertResponseContainValues($values, $response)
213
    {
214
        if (!is_array($values)) {
215
            $values = (array)$values;
216
        }
217
218
        foreach ($values as $value) {
219
            $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...
220
        }
221
    }
222
223
    /**
224
     * @param $data
225
     * @param $response
226
     */
227
    public function assertResponseContainKeyValue($data, $response)
228
    {
229
        $response = json_encode(LaravelArr::sortRecursive(
230
            (array)$this->responseToArray($response)
231
        ));
232
233
        foreach (LaravelArr::sortRecursive($data) as $key => $value) {
234
            $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...
235
            $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...
236
                "The JSON fragment [ {$expected} ] does not exist in the response [ {$response} ].");
237
        }
238
    }
239
240
    /**
241
     * Prettify printing JSON data (mainly for API responses) in the terminal.
242
     *
243
     * @param $json
244
     *
245
     * @return string
246
     */
247
    public function ddj($json)
248
    {
249
        $result = '';
250
        $pos = 0;
251
        $strLen = strlen($json);
252
        $indentStr = '  ';
253
        $newLine = "\n";
254
        $prevChar = '';
255
        $outOfQuotes = true;
256
257
        for ($i = 0; $i <= $strLen; ++$i) {
258
259
            // Grab the next character in the string.
260
            $char = substr($json, $i, 1);
261
262
            // Are we inside a quoted string?
263
            if ($char == '"' && $prevChar != '\\') {
264
                $outOfQuotes = !$outOfQuotes;
265
266
                // If this character is the end of an element,
267
                // output a new line and indent the next line.
268
            } else {
269
                if (($char == '}' || $char == ']') && $outOfQuotes) {
270
                    $result .= $newLine;
271
                    --$pos;
272
                    for ($j = 0; $j < $pos; ++$j) {
273
                        $result .= $indentStr;
274
                    }
275
                }
276
            }
277
278
            // Add the character to the result string.
279
            $result .= $char;
280
281
            // If the last character was the beginning of an element,
282
            // output a new line and indent the next line.
283
            if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) {
284
                $result .= $newLine;
285
                if ($char == '{' || $char == '[') {
286
                    ++$pos;
287
                }
288
289
                for ($j = 0; $j < $pos; ++$j) {
290
                    $result .= $indentStr;
291
                }
292
            }
293
294
            $prevChar = $char;
295
        }
296
297
        dd($result);
298
    }
299
300
    /**
301
     * Migrate the database.
302
     */
303
    public function migrateDatabase()
304
    {
305
        \Artisan::call('migrate');
306
    }
307
308
    /**
309
     * @param $response
310
     *
311
     * @return mixed
312
     */
313
    private function responseToArray($response)
314
    {
315
        if ($response instanceof \Illuminate\Http\Response) {
316
            $response = json_decode($response->getContent(), true);
317
        }
318
319
        if (array_key_exists('data', $response)) {
320
            $response = $response['data'];
321
        }
322
323
        return $response;
324
    }
325
326
    /**
327
     * Format the given key and value into a JSON string for expectation checks.
328
     *
329
     * @param string $key
330
     * @param mixed  $value
331
     *
332
     * @return string
333
     */
334
    private function formatToKeyValueToString($key, $value)
335
    {
336
        $expected = json_encode([$key => $value]);
337
338
        if (LaravelStr::startsWith($expected, '{')) {
339
            $expected = substr($expected, 1);
340
        }
341
342
        if (LaravelStr::endsWith($expected, '}')) {
343
            $expected = substr($expected, 0, -1);
344
        }
345
346
        return $expected;
347
    }
348
349
    /**
350
     * Mocking helper
351
     *
352
     * @param $class
353
     *
354
     * @return  \Mockery\MockInterface
355
     */
356
    public function mock($class)
357
    {
358
        $mock = Mockery::mock($class);
359
        $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...
360
361
        return $mock;
362
    }
363
364
    /**
365
     * get response object, get the string content from it and convert it to an std object
366
     * making it easier to read
367
     *
368
     * @param $response
369
     *
370
     * @return  mixed
371
     */
372
    public function getResponseObject(Response $response)
373
    {
374
        return json_decode($response->getContent());
375
    }
376
377
}
378