Completed
Push — master ( 4d6b90...85818a )
by Mahmoud
03:14
created

TestingTrait::assertResponseContainKeys()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
rs 9.4285
cc 3
eloc 5
nc 4
nop 2
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\Support\Arr as LaravelArr;
9
use Illuminate\Support\Str as LaravelStr;
10
use Mockery;
11
use Symfony\Component\Debug\Exception\UndefinedMethodException;
12
13
/**
14
 * Class TestingTrait.
15
 *
16
 * All the functions in this trait are accessible from all your tests.
17
 *
18
 * @author  Mahmoud Zalt <[email protected]>
19
 */
20
trait TestingTrait
21
{
22
23
    /**
24
     * the Logged in user, used for protected routes.
25
     *
26
     * @var User
27
     */
28
    public $loggedInTestingUser;
29
30
    /**
31
     * @param           $endpoint
32
     * @param string    $verb
33
     * @param array     $data
34
     * @param bool|true $protected
35
     * @param array     $header
36
     *
37
     * @throws \Symfony\Component\Debug\Exception\UndefinedMethodException
38
     *
39
     * @return mixed
40
     */
41
    public function apiCall($endpoint, $verb = 'get', array $data = [], $protected = true, array $header = [])
42
    {
43
        $content = json_encode($data);
44
45
        $headers = array_merge([
46
            'CONTENT_LENGTH' => mb_strlen($content, '8bit'),
47
//            '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...
48
            'Accept'         => 'application/json',
49
        ], $header);
50
51
52
        // if endpoint is protected (requires token to access it's functionality)
53
        if ($protected && !array_has($header, 'Authorization')) {
54
            // append the token to the header
55
            $headers['Authorization'] = 'Bearer ' . $this->getLoggedInTestingUserToken();
56
        }
57
58
        if (!$protected && !array_has($header, 'Agent-Id')) {
59
            // append the Device ID to the header (IPhone UUID, Android ID, ...)
60
            $headers['Agent-Id'] = str_random(40);
61
        }
62
63
        $verb = strtolower($verb);
64
65
        switch ($verb) {
66
            case 'get':
67
                $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...
68
                break;
69
            case 'post':
70
            case 'put':
71
            case 'patch':
72
            case 'delete':
73
                $response = $this->{$verb}($endpoint, $data, $headers)->response;
74
                break;
75
            default:
76
                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...
77
        }
78
79
        return $response;
80
    }
81
82
    /**
83
     * @param \Dingo\Api\Http\Response $response
84
     * @param array                    $messages
85
     */
86
    public function assertValidationErrorContain(DingoAPIResponse $response, array $messages)
87
    {
88
89
        $arrayResponse = json_decode($response->getContent());
90
91
        foreach ($messages as $key => $value) {
92
            $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...
93
        }
94
    }
95
96
    /**
97
     * get teh current logged in user.
98
     *
99
     * @return \App\Port\Tests\PHPUnit\Traits\User|mixed
100
     */
101
    public function getLoggedInTestingUser()
102
    {
103
        $user = $this->loggedInTestingUser;
104
105
        if (!$user) {
106
            $user = $this->registerAndLoginTestingUser();
107
        }
108
109
        return $user;
110
    }
111
112
    /**
113
     * get teh current logged in user token.
114
     *
115
     * @return string
116
     */
117
    public function getLoggedInTestingUserToken()
118
    {
119
        return $this->getLoggedInTestingUser()->token;
120
    }
121
122
    /**
123
     * @param null $userDetails
124
     *
125
     * @return mixed
126
     */
127
    public function registerAndLoginTestingUser($userDetails = null)
128
    {
129
        // if no user detail provided, use the default details.
130
        if (!$userDetails) {
131
            $userDetails = [
132
                'name'     => 'Mahmoud Zalt',
133
                'email'    => '[email protected]',
134
                'password' => 'secret.Pass7',
135
            ];
136
        }
137
138
        $RegisterUserAction = App::make(RegisterUserAction::class);
139
140
        // 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...
141
        $user = $RegisterUserAction->run(
142
            $userDetails['email'],
143
            $userDetails['password'],
144
            $userDetails['name'],
145
            true
146
        );
147
148
        return $this->loggedInTestingUser = $user;
149
    }
150
151
    /**
152
     * @param $keys
153
     * @param $response
154
     */
155
    public function assertResponseContainKeys($keys, $response)
156
    {
157
        if (!is_array($keys)) {
158
            $keys = (array)$keys;
159
        }
160
161
        foreach ($keys as $key) {
162
            $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...
163
        }
164
    }
165
166
    /**
167
     * @param $values
168
     * @param $response
169
     */
170
    public function assertResponseContainValues($values, $response)
171
    {
172
        if (!is_array($values)) {
173
            $values = (array)$values;
174
        }
175
176
        foreach ($values as $value) {
177
            $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...
178
        }
179
    }
180
181
    /**
182
     * @param $data
183
     * @param $response
184
     */
185
    public function assertResponseContainKeyValue($data, $response)
186
    {
187
        $response = json_encode(LaravelArr::sortRecursive(
188
            (array)$this->responseToArray($response)
189
        ));
190
191
        foreach (LaravelArr::sortRecursive($data) as $key => $value) {
192
            $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...
193
            $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...
194
                "The JSON fragment [ {$expected} ] does not exist in the response [ {$response} ].");
195
        }
196
    }
197
198
    /**
199
     * Prettify printing JSON data (mainly for API responses) in the terminal.
200
     *
201
     * @param $json
202
     *
203
     * @return string
204
     */
205
    public function ddj($json)
206
    {
207
        $result = '';
208
        $pos = 0;
209
        $strLen = strlen($json);
210
        $indentStr = '  ';
211
        $newLine = "\n";
212
        $prevChar = '';
213
        $outOfQuotes = true;
214
215
        for ($i = 0; $i <= $strLen; ++$i) {
216
217
            // Grab the next character in the string.
218
            $char = substr($json, $i, 1);
219
220
            // Are we inside a quoted string?
221
            if ($char == '"' && $prevChar != '\\') {
222
                $outOfQuotes = !$outOfQuotes;
223
224
                // If this character is the end of an element,
225
                // output a new line and indent the next line.
226
            } else {
227
                if (($char == '}' || $char == ']') && $outOfQuotes) {
228
                    $result .= $newLine;
229
                    --$pos;
230
                    for ($j = 0; $j < $pos; ++$j) {
231
                        $result .= $indentStr;
232
                    }
233
                }
234
            }
235
236
            // Add the character to the result string.
237
            $result .= $char;
238
239
            // If the last character was the beginning of an element,
240
            // output a new line and indent the next line.
241
            if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) {
242
                $result .= $newLine;
243
                if ($char == '{' || $char == '[') {
244
                    ++$pos;
245
                }
246
247
                for ($j = 0; $j < $pos; ++$j) {
248
                    $result .= $indentStr;
249
                }
250
            }
251
252
            $prevChar = $char;
253
        }
254
255
        dd($result);
256
    }
257
258
    /**
259
     * Migrate the database.
260
     */
261
    public function migrateDatabase()
262
    {
263
        \Artisan::call('migrate');
264
    }
265
266
    /**
267
     * @param $response
268
     *
269
     * @return mixed
270
     */
271
    private function responseToArray($response)
272
    {
273
        if ($response instanceof \Illuminate\Http\Response) {
274
            $response = json_decode($response->getContent(), true);
275
        }
276
277
        if (array_key_exists('data', $response)) {
278
            $response = $response['data'];
279
        }
280
281
        return $response;
282
    }
283
284
    /**
285
     * Format the given key and value into a JSON string for expectation checks.
286
     *
287
     * @param string $key
288
     * @param mixed  $value
289
     *
290
     * @return string
291
     */
292
    private function formatToKeyValueToString($key, $value)
293
    {
294
        $expected = json_encode([$key => $value]);
295
296
        if (LaravelStr::startsWith($expected, '{')) {
297
            $expected = substr($expected, 1);
298
        }
299
300
        if (LaravelStr::endsWith($expected, '}')) {
301
            $expected = substr($expected, 0, -1);
302
        }
303
304
        return $expected;
305
    }
306
307
    /**
308
     * Mocking helper
309
     *
310
     * @param $class
311
     *
312
     * @return  \Mockery\MockInterface
313
     */
314
    public function mock($class)
315
    {
316
        $mock = Mockery::mock($class);
317
        $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...
318
319
        return $mock;
320
    }
321
}
322