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->dataDescription()); |
||
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
Bug
introduced
by
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 |