Issues (281)

Branch: master

src/Common/WebTestCase.php (1 issue)

1
<?php
2
3
namespace Common;
4
5
use ForkCMS\App\AppKernel;
6
use ForkCMS\App\BaseModel;
7
use SpoonDatabase;
8
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase;
9
use Symfony\Component\FileSystem\Filesystem;
10
use Symfony\Bundle\FrameworkBundle\Client;
11
use Symfony\Component\DomCrawler\Form;
12
use Symfony\Component\DomCrawler\Crawler;
13
use Backend\Core\Engine\Authentication;
14
use Symfony\Component\HttpFoundation\Response;
15
16
/**
17
 * WebTestCase is the base class for functional tests.
18
 */
19
abstract class WebTestCase extends BaseWebTestCase
20
{
21
    protected $preserveGlobalState = false;
22
    protected $runTestInSeparateProcess = true;
23
24
    protected function setUp(): void
25
    {
26
        parent::setUp();
27
28
        if (!defined('LANGUAGE')) {
29
            define('LANGUAGE', 'en');
30
        }
31
32
        if (!defined('FRONTEND_LANGUAGE')) {
33
            define('FRONTEND_LANGUAGE', 'en');
34
        }
35
36
        // Inject the client in the data
37
        $client = static::createClient();
38
        $data = $this->getProvidedData();
39
        $data[] = $client;
40
        $this->__construct($this->getName(), $data, $this->dataName());
41
42
        $this->resetDataBase($client);
43
    }
44
45
    /**
46
     * Attempts to guess the kernel location.
47
     *
48
     * When the Kernel is located, the file is required.
49
     *
50
     * @return string The Kernel class name
51
     * @throws \RuntimeException
52
     *
53
     * @todo Remove this when Fork has no custom Kernel class anymore
54
     *
55
     */
56
    protected static function getKernelClass(): string
57
    {
58
        return AppKernel::class;
59
    }
60
61
    /**
62
     * Creates a Client.
63
     *
64
     * @param array $options An array of options to pass to the createKernel class
65
     * @param array $server An array of server parameters
66
     *
67
     * @return Client A Client instance
68
     */
69
    protected static function createClient(array $options = [], array $server = []): Client
70
    {
71
        if (null !== static::$kernel) {
72
            static::$kernel->shutdown();
73
        }
74
75
        if (!array_key_exists('environment', $options)) {
76
            $options['environment'] = 'test';
77
        }
78
79
        $client = parent::createClient($options, $server);
80
        static::$kernel = $client->getKernel();
81
        BaseModel::setContainer(static::$kernel->getContainer());
82
83
        return $client;
84
    }
85
86
    /**
87
     * Fully empties the test database
88
     *
89
     * @param SpoonDatabase $database
90
     */
91
    protected function emptyTestDatabase(SpoonDatabase $database): void
92
    {
93
        foreach ($database->getTables() as $table) {
94
            $database->execute(
95
                'SET FOREIGN_KEY_CHECKS = 0; DROP TABLE IF EXISTS ' . $table . '; SET FOREIGN_KEY_CHECKS = 1;'
96
            );
97
        }
98
    }
99
100
    /**
101
     * Executes sql in the database
102
     *
103
     * @param SpoonDatabase $database
104
     * @param string $sql
105
     */
106
    protected function importSQL(SpoonDatabase $database, string $sql): void
107
    {
108
        $database->execute(trim($sql));
109
    }
110
111
    protected function resetDataBase(Client $client): void
112
    {
113
        $database = $client->getContainer()->get('database');
114
115
        // make sure our database has a clean state (freshly installed Fork)
116
        $this->emptyTestDatabase($database);
117
        $kernelDir = $client->getContainer()->getParameter('kernel.project_dir') . '/app';
118
        $this->importSQL(
119
            $database,
0 ignored issues
show
It seems like $database can also be of type null; however, parameter $database of Common\WebTestCase::importSQL() does only seem to accept SpoonDatabase, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

119
            /** @scrutinizer ignore-type */ $database,
Loading history...
120
            file_get_contents($kernelDir . '/../tests/data/test_db.sql')
121
        );
122
    }
123
124
    protected function loadFixtures(Client $client, array $fixtureClasses = []): void
125
    {
126
        $database = $client->getContainer()->get('database');
127
128
        // load all the fixtures
129
        foreach ($fixtureClasses as $class) {
130
            $fixture = new $class();
131
            $fixture->load($database);
132
        }
133
    }
134
135
    /**
136
     * Copies the parameters.yml file to a backup version
137
     *
138
     * @param string $kernelDir
139
     * @param Filesystem $filesystem
140
     */
141
    protected function backupParametersFile(Filesystem $filesystem, string $kernelDir): void
142
    {
143
        if ($filesystem->exists($kernelDir . '/config/parameters.yml')) {
144
            $filesystem->copy(
145
                $kernelDir . '/config/parameters.yml',
146
                $kernelDir . '/config/parameters.yml~backup'
147
            );
148
        }
149
150
        if ($filesystem->exists($kernelDir . '/../var/cache/test')) {
151
            $filesystem->remove($kernelDir . '/../var/cache/test');
152
        }
153
154
        if ($filesystem->exists($kernelDir . '/../var/cache/test_install')) {
155
            $filesystem->remove($kernelDir . '/../var/cache/test_install');
156
        }
157
    }
158
159
    /**
160
     * Puts the backed up parameters.yml file back
161
     *
162
     * @param string $kernelDir
163
     * @param Filesystem $filesystem
164
     */
165
    protected function putParametersFileBack(Filesystem $filesystem, string $kernelDir): void
166
    {
167
        if ($filesystem->exists($kernelDir . '/config/parameters.yml~backup')) {
168
            $filesystem->copy(
169
                $kernelDir . '/config/parameters.yml~backup',
170
                $kernelDir . '/config/parameters.yml',
171
                true
172
            );
173
            $filesystem->remove($kernelDir . '/config/parameters.yml~backup');
174
        }
175
        if ($filesystem->exists($kernelDir . '/cache/test')) {
176
            $filesystem->remove($kernelDir . '/cache/test');
177
        }
178
    }
179
180
    protected static function assertIs404(Client $client): void
181
    {
182
        self::assertEquals(
183
            Response::HTTP_NOT_FOUND,
184
            $client->getResponse()->getStatusCode()
185
        );
186
    }
187
188
    protected static function assertIs200(Client $client): void
189
    {
190
        self::assertEquals(
191
            Response::HTTP_OK,
192
            $client->getResponse()->getStatusCode()
193
        );
194
    }
195
196
    /**
197
     * Submits the form and mimics the GET parameters, since they aren't added
198
     * by default in the functional tests
199
     *
200
     * @param Client $client
201
     * @param Form $form
202
     * @param array $data
203
     * @param bool $setValues set to true for symfony @TODO set default true in Fork 6
204
     */
205
    protected function submitForm(Client $client, Form $form, array $data = [], bool $setValues = false): void
206
    {
207
        $values = $data;
208
        // @TODO remove this once SpoonForm has been removed
209
        if (!$setValues) {
210
            // Get parameters should be set manually. Symfony uses the request object,
211
            // but spoon still checks the $_GET and $_POST parameters
212
            foreach ($data as $key => $value) {
213
                $_GET[$key] = $value;
214
                $_POST[$key] = $value;
215
            }
216
217
            $values = [];
218
        }
219
220
        $client->submit($form, $values);
221
222
        // @TODO remove this once SpoonForm has been removed
223
        if (!$setValues) {
224
            foreach ($data as $key => $value) {
225
                unset($_GET[$key]);
226
                unset($_POST[$key]);
227
            }
228
        }
229
    }
230
231
    /**
232
     * Edits the data of a form
233
     *
234
     * @param Client $client
235
     * @param Form $form
236
     * @param array $data
237
     */
238
    protected function submitEditForm(Client $client, Form $form, array $data = []): void
239
    {
240
        $originalData = [];
241
        foreach ($form->all() as $fieldName => $formField) {
242
            $originalData[$fieldName] = $formField->getValue();
243
        }
244
245
        $data = array_merge($originalData, $data);
246
247
        $this->submitForm($client, $form, $data);
248
    }
249
250
    /**
251
     * Do a request with the given GET parameters
252
     *
253
     * @param Client $client
254
     * @param string $url
255
     * @param array $data
256
     *
257
     * @return Crawler
258
     */
259
    protected function requestWithGetParameters(
260
        Client $client,
261
        string $url,
262
        array $data = []
263
    ): Crawler {
264
        $this->setGetParameters($data);
265
        $request = $client->request('GET', $url, $data);
266
        $this->unsetGetParameters($data);
267
268
        return $request;
269
    }
270
271
    /**
272
     * Set the GET parameters, as some of the old code relies on GET
273
     *
274
     * @param array $data
275
     */
276
    protected function setGetParameters(array $data = []): void
277
    {
278
        foreach ((array) $data as $key => $value) {
279
            $_GET[$key] = $value;
280
        }
281
    }
282
283
    /**
284
     * Unset the GET parameters, as some of the old code relies on GET
285
     *
286
     * @param array $data
287
     */
288
    protected function unsetGetParameters(array $data = []): void
289
    {
290
        if (empty($data)) {
291
            $_GET = [];
292
293
            return;
294
        }
295
296
        foreach ($data as $key => $value) {
297
            unset($_GET[$key]);
298
        }
299
    }
300
301
    /**
302
     * Logs the client in
303
     *
304
     * Logging in using the forms is tested in the Authentication module
305
     *
306
     * @param Client $client
307
     */
308
    protected function login(Client $client): void
309
    {
310
        $this->logout($client);
311
        self::assertHttpStatusCode200($client, '/private/en/authentication');
312
        $form = $this->getFormForSubmitButton($client, 'login');
313
        $this->submitForm(
314
            $client,
315
            $form,
316
            [
317
                'form' => 'authenticationIndex',
318
                'backend_email' => '[email protected]',
319
                'backend_password' => 'fork',
320
                'form_token' => $form['form_token']->getValue(),
321
            ]
322
        );
323
    }
324
325
    /**
326
     * Logs the client out
327
     *
328
     * @param Client $client
329
     */
330
    protected function logout(Client $client): void
331
    {
332
        $client->setMaxRedirects(-1);
333
        $client->request('GET', '/private/en/authentication/logout');
334
        Authentication::tearDown();
335
    }
336
337
    protected static function assertGetsRedirected(
338
        Client $client,
339
        string $initialUrl,
340
        string $expectedUrl,
341
        string $requestMethod = 'GET',
342
        array $requestParameters = [],
343
        int $maxRedirects = null,
344
        int $expectedHttpResponseCode = Response::HTTP_OK
345
    ): void {
346
        $maxRedirects !== null ? $client->setMaxRedirects($maxRedirects) : $client->followRedirects();
347
348
        $client->request($requestMethod, $initialUrl, $requestParameters);
349
350
        $response = $client->getResponse();
351
        self::assertNotNull($response, 'No response received');
352
353
        self::assertCurrentUrlContains($client, $expectedUrl);
354
        self::assertEquals($expectedHttpResponseCode, $response->getStatusCode());
355
    }
356
357
    /**
358
     * @param Client $client
359
     * @param string $url
360
     * @param string[] $expectedContent
361
     * @param int $httpStatusCode
362
     * @param string $requestMethod
363
     * @param array $requestParameters
364
     */
365
    protected static function assertPageLoadedCorrectly(
366
        Client $client,
367
        string $url,
368
        array $expectedContent,
369
        int $httpStatusCode = Response::HTTP_OK,
370
        string $requestMethod = 'GET',
371
        array $requestParameters = []
372
    ): void {
373
        self::assertHttpStatusCode($client, $url, $httpStatusCode, $requestMethod, $requestParameters);
374
        $response = $client->getResponse();
375
376
        self::assertNotNull($response, 'No response received');
377
        self::assertResponseHasContent($response, ...$expectedContent);
378
    }
379
380
    /**
381
     * @param Client $client
382
     * @param string $linkText
383
     * @param string[] $expectedContent
384
     * @param int $httpStatusCode
385
     * @param string $requestMethod
386
     * @param array $requestParameters
387
     */
388
    protected static function assertClickOnLink(
389
        Client $client,
390
        string $linkText,
391
        array $expectedContent,
392
        int $httpStatusCode = Response::HTTP_OK,
393
        string $requestMethod = 'GET',
394
        array $requestParameters = []
395
    ): void {
396
        self::assertPageLoadedCorrectly(
397
            $client,
398
            $client->getCrawler()->selectLink($linkText)->link()->getUri(),
399
            $expectedContent,
400
            $httpStatusCode,
401
            $requestMethod,
402
            $requestParameters
403
        );
404
    }
405
406
    protected static function assertResponseHasContent(Response $response, string ...$content): void
407
    {
408
        foreach ($content as $expectedContent) {
409
            self::assertStringContainsString($expectedContent, $response->getContent());
410
        }
411
    }
412
413
    protected static function assertResponseDoesNotHaveContent(Response $response, string ...$content): void
414
    {
415
        foreach ($content as $notExpectedContent) {
416
            self::assertStringNotContainsString($notExpectedContent, $response->getContent());
417
        }
418
    }
419
420
    protected static function assertCurrentUrlContains(Client $client, string ...$partialUrls): void
421
    {
422
        foreach ($partialUrls as $partialUrl) {
423
            self::assertStringContainsString($partialUrl, $client->getHistory()->current()->getUri());
424
        }
425
    }
426
427
    protected static function assertCurrentUrlEndsWith(Client $client, string $partialUrl): void
428
    {
429
        self::assertStringEndsWith($partialUrl, $client->getHistory()->current()->getUri());
430
    }
431
432
    protected static function assertHttpStatusCode(
433
        Client $client,
434
        string $url,
435
        int $httpStatusCode,
436
        string $requestMethod = 'GET',
437
        array $requestParameters = []
438
    ): void {
439
        $client->request($requestMethod, $url, $requestParameters);
440
        $response = $client->getResponse();
441
        self::assertNotNull($response, 'No response received');
442
        self::assertEquals($httpStatusCode, $response->getStatusCode());
443
    }
444
445
    protected static function assertHttpStatusCode200(
446
        Client $client,
447
        string $url,
448
        string $requestMethod = 'GET',
449
        array $requestParameters = []
450
    ): void {
451
        self::assertHttpStatusCode(
452
            $client,
453
            $url,
454
            Response::HTTP_OK,
455
            $requestMethod,
456
            $requestParameters
457
        );
458
    }
459
460
    protected static function assertHttpStatusCode404(
461
        Client $client,
462
        string $url,
463
        string $requestMethod = 'GET',
464
        array $requestParameters = []
465
    ): void {
466
        self::assertHttpStatusCode(
467
            $client,
468
            $url,
469
            Response::HTTP_NOT_FOUND,
470
            $requestMethod,
471
            $requestParameters
472
        );
473
    }
474
475
    protected function getFormForSubmitButton(Client $client, string $buttonText, string $filterSelector = null): Form
476
    {
477
        $crawler = $client->getCrawler();
478
479
        if ($filterSelector !== null) {
480
            $crawler = $crawler->filter($filterSelector);
481
        }
482
483
        return $crawler->selectButton($buttonText)->form();
484
    }
485
}
486