Completed
Push — master ( 72f844...115453 )
by Mahmoud
04:40
created

TestingTrait::getResponseObject()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
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
                $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...
69
                break;
70
            case 'post':
71
            case 'put':
72
            case 'patch':
73
            case 'delete':
74
                $response = $this->{$verb}($endpoint, $data, $headers)->response;
75
                break;
76
            default:
77
                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...
78
        }
79
80
        return $response;
81
    }
82
83
    /**
84
     * @param \Dingo\Api\Http\Response $response
85
     * @param array                    $messages
86
     */
87
    public function assertValidationErrorContain(DingoAPIResponse $response, array $messages)
88
    {
89
90
        $arrayResponse = json_decode($response->getContent());
91
92
        foreach ($messages as $key => $value) {
93
            $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...
94
        }
95
    }
96
97
    /**
98
     * get teh current logged in user.
99
     *
100
     * @return \App\Port\Tests\PHPUnit\Traits\User|mixed
101
     */
102
    public function getLoggedInTestingUser()
103
    {
104
        $user = $this->loggedInTestingUser;
105
106
        if (!$user) {
107
            $user = $this->registerAndLoginTestingUser();
108
        }
109
110
        return $user;
111
    }
112
113
    /**
114
     * get teh current logged in user token.
115
     *
116
     * @return string
117
     */
118
    public function getLoggedInTestingUserToken()
119
    {
120
        return $this->getLoggedInTestingUser()->token;
121
    }
122
123
    /**
124
     * @param null $userDetails
125
     *
126
     * @return mixed
127
     */
128
    public function registerAndLoginTestingUser($userDetails = null)
129
    {
130
        // if no user detail provided, use the default details.
131
        if (!$userDetails) {
132
            $userDetails = [
133
                'name'     => 'Mahmoud Zalt',
134
                'email'    => '[email protected]',
135
                'password' => 'secret.Pass7',
136
            ];
137
        }
138
139
        $RegisterUserAction = App::make(RegisterUserAction::class);
140
141
        // 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...
142
        $user = $RegisterUserAction->run(
143
            $userDetails['email'],
144
            $userDetails['password'],
145
            $userDetails['name'],
146
            true
147
        );
148
149
        return $this->loggedInTestingUser = $user;
150
    }
151
152
    /**
153
     * @param $keys
154
     * @param $response
155
     */
156
    public function assertResponseContainKeys($keys, $response)
157
    {
158
        if (!is_array($keys)) {
159
            $keys = (array)$keys;
160
        }
161
162
        foreach ($keys as $key) {
163
            $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...
164
        }
165
    }
166
167
    /**
168
     * @param $values
169
     * @param $response
170
     */
171
    public function assertResponseContainValues($values, $response)
172
    {
173
        if (!is_array($values)) {
174
            $values = (array)$values;
175
        }
176
177
        foreach ($values as $value) {
178
            $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...
179
        }
180
    }
181
182
    /**
183
     * @param $data
184
     * @param $response
185
     */
186
    public function assertResponseContainKeyValue($data, $response)
187
    {
188
        $response = json_encode(LaravelArr::sortRecursive(
189
            (array)$this->responseToArray($response)
190
        ));
191
192
        foreach (LaravelArr::sortRecursive($data) as $key => $value) {
193
            $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...
194
            $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...
195
                "The JSON fragment [ {$expected} ] does not exist in the response [ {$response} ].");
196
        }
197
    }
198
199
    /**
200
     * Prettify printing JSON data (mainly for API responses) in the terminal.
201
     *
202
     * @param $json
203
     *
204
     * @return string
205
     */
206
    public function ddj($json)
207
    {
208
        $result = '';
209
        $pos = 0;
210
        $strLen = strlen($json);
211
        $indentStr = '  ';
212
        $newLine = "\n";
213
        $prevChar = '';
214
        $outOfQuotes = true;
215
216
        for ($i = 0; $i <= $strLen; ++$i) {
217
218
            // Grab the next character in the string.
219
            $char = substr($json, $i, 1);
220
221
            // Are we inside a quoted string?
222
            if ($char == '"' && $prevChar != '\\') {
223
                $outOfQuotes = !$outOfQuotes;
224
225
                // If this character is the end of an element,
226
                // output a new line and indent the next line.
227
            } else {
228
                if (($char == '}' || $char == ']') && $outOfQuotes) {
229
                    $result .= $newLine;
230
                    --$pos;
231
                    for ($j = 0; $j < $pos; ++$j) {
232
                        $result .= $indentStr;
233
                    }
234
                }
235
            }
236
237
            // Add the character to the result string.
238
            $result .= $char;
239
240
            // If the last character was the beginning of an element,
241
            // output a new line and indent the next line.
242
            if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) {
243
                $result .= $newLine;
244
                if ($char == '{' || $char == '[') {
245
                    ++$pos;
246
                }
247
248
                for ($j = 0; $j < $pos; ++$j) {
249
                    $result .= $indentStr;
250
                }
251
            }
252
253
            $prevChar = $char;
254
        }
255
256
        dd($result);
257
    }
258
259
    /**
260
     * Migrate the database.
261
     */
262
    public function migrateDatabase()
263
    {
264
        \Artisan::call('migrate');
265
    }
266
267
    /**
268
     * @param $response
269
     *
270
     * @return mixed
271
     */
272
    private function responseToArray($response)
273
    {
274
        if ($response instanceof \Illuminate\Http\Response) {
275
            $response = json_decode($response->getContent(), true);
276
        }
277
278
        if (array_key_exists('data', $response)) {
279
            $response = $response['data'];
280
        }
281
282
        return $response;
283
    }
284
285
    /**
286
     * Format the given key and value into a JSON string for expectation checks.
287
     *
288
     * @param string $key
289
     * @param mixed  $value
290
     *
291
     * @return string
292
     */
293
    private function formatToKeyValueToString($key, $value)
294
    {
295
        $expected = json_encode([$key => $value]);
296
297
        if (LaravelStr::startsWith($expected, '{')) {
298
            $expected = substr($expected, 1);
299
        }
300
301
        if (LaravelStr::endsWith($expected, '}')) {
302
            $expected = substr($expected, 0, -1);
303
        }
304
305
        return $expected;
306
    }
307
308
    /**
309
     * Mocking helper
310
     *
311
     * @param $class
312
     *
313
     * @return  \Mockery\MockInterface
314
     */
315
    public function mock($class)
316
    {
317
        $mock = Mockery::mock($class);
318
        $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...
319
320
        return $mock;
321
    }
322
323
    /**
324
     * get response object, get the string content from it and convert it to an std object
325
     * making it easier to read
326
     *
327
     * @param $response
328
     *
329
     * @return  mixed
330
     */
331
    public function getResponseObject(Response $response)
332
    {
333
        return json_decode($response->getContent());
334
    }
335
336
}
337