Completed
Pull Request — 3.1 (#347)
by Łukasz
16:49 queued 11:36
created

DataContext::followingNewsExistInDatabase()   B

Complexity

Conditions 8
Paths 13

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 39
rs 8.0515
c 0
b 0
f 0
cc 8
nc 13
nop 1
1
<?php
2
3
/**
4
 * (c) FSi sp. z o.o. <[email protected]>
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace FSi\Bundle\AdminBundle\Behat\Context;
13
14
use Behat\Gherkin\Node\TableNode;
15
use DateTime;
16
use Doctrine\ORM\Tools\SchemaTool;
17
use FSi\FixturesBundle\Entity\Category;
18
use FSi\FixturesBundle\Entity\News;
19
use FSi\FixturesBundle\Entity\Person;
20
use FSi\FixturesBundle\Entity\Subscriber;
21
use FSi\FixturesBundle\Entity\Tag;
22
use InvalidArgumentException;
23
use RuntimeException;
24
use Symfony\Component\PropertyAccess\PropertyAccess;
25
use Symfony\Component\PropertyAccess\PropertyAccessor;
26
27
use function expect;
28
use function file_exists;
29
30
class DataContext extends AbstractContext
31
{
32
    /**
33
     * @var PropertyAccessor|null
34
     */
35
    private $propertyAccessor = null;
36
37
    /**
38
     * @BeforeScenario
39
     */
40
    public function createDatabase(): void
41
    {
42
        $this->deleteDatabaseIfExist();
43
44
        $entityManager = $this->getEntityManager();
45
        $metadata = $entityManager->getMetadataFactory()->getAllMetadata();
46
47
        $tool = new SchemaTool($entityManager);
48
        $tool->createSchema($metadata);
49
    }
50
51
    /**
52
     * @AfterScenario
53
     */
54
    public function deleteDatabaseIfExist(): void
55
    {
56
        $dbFilePath = $this->getKernel()->getRootDir() . '/data.sqlite';
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\HttpKe...Interface::getRootDir() has been deprecated with message: since Symfony 4.2

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
57
        if (true === file_exists($dbFilePath)) {
58
            unlink($dbFilePath);
59
        }
60
    }
61
62
    /**
63
     * @Transform :className
64
     */
65
    public function entityToClassName(string $entityName): string
66
    {
67
        switch ($entityName) {
68
            case 'news':
69
                return News::class;
70
            case 'category':
71
            case 'categories':
72
                return Category::class;
73
            case 'subscriber':
74
            case 'subscribers':
75
                return Subscriber::class;
76
            case 'person':
77
                return Person::class;
78
        }
79
    }
80
81
    /**
82
     * @Given there are :count :className
83
     * @Given there is :count :className
84
     */
85
    public function thereIsNumberOfEntities(int $count, string $className)
86
    {
87
        $entityManager = $this->getEntityManager();
88
        for ($i = 0; $i < $count; $i++) {
89
            $instance = new $className();
90
            $this->applyEntityModifiers($instance);
91
            $this->applyFieldFormatters($instance);
92
            $entityManager->persist($instance);
93
        }
94
95
        $entityManager->flush();
96
97
        expect(count($this->getRepository($className)->findAll()))->toBe($count);
98
    }
99
100
    /**
101
     * @Given there is a :className with :field :value present in the database
102
     */
103
    public function thereIsAnEntityWithField(string $className, string $field, $value)
104
    {
105
        $instance = new $className();
106
        $formatters = $this->getFieldFormatters($className);
107
        $formatters[$field] = function () use ($value) {
108
            return $this->parseScenarioValue((string) $value);
109
        };
110
        $this->applyEntityModifiers($instance);
111
        $this->applyFieldFormatters($instance, $formatters);
112
113
        $entityManager = $this->getEntityManager();
114
        $entityManager->persist($instance);
115
        $entityManager->flush();
116
117
        expect($this->getRepository($className)->findOneBy([$field => $value]))->toBeAnInstanceOf($className);
0 ignored issues
show
Bug introduced by
The method toBeAnInstanceOf() does not exist on FriendsOfPhpSpec\PhpSpec\Expect\Subject. Did you maybe mean beAnInstanceOf()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
118
    }
119
120
    /**
121
     * @Then there should be a :className with :field :value present in the database
122
     */
123
    public function entityWithFieldShouldExist(string $className, string $field, $value)
124
    {
125
        expect($this->getRepository($className)->findOneBy([$field => $value]))->toBeAnInstanceOf($className);
0 ignored issues
show
Bug introduced by
The method toBeAnInstanceOf() does not exist on FriendsOfPhpSpec\PhpSpec\Expect\Subject. Did you maybe mean beAnInstanceOf()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
126
    }
127
128
    /**
129
     * @Given :className with :field :value should not exist in database anymore
130
     */
131
    public function entityShouldNotExistInDatabaseAnymore(string $className, string $field, $value)
132
    {
133
        expect($this->getRepository($className)->findOneBy([$field => $value]))->toBe(null);
134
    }
135
136
    /**
137
     * @Then new :className should be created
138
     */
139
    public function newEntityShouldBeCreated(string $className)
140
    {
141
        $this->thereShouldExistsNumberOfEntities(1, $className);
142
    }
143
144
    /**
145
     * @Then there should not be any :className present in the database
146
     */
147
    public function thereShouldNotBeAnyEntities(string $className)
148
    {
149
        $this->thereShouldExistsNumberOfEntities(0, $className);
150
    }
151
152
    /**
153
     * @Then there should be :count :className present in the database
154
     */
155
    public function thereShouldExistsNumberOfEntities($count, string $className)
156
    {
157
        expect(count($this->getRepository($className)->findAll()))->toBe($count);
158
    }
159
160
    /**
161
     * @Given /^the following news exist in database$/
162
     */
163
    public function followingNewsExistInDatabase(TableNode $table)
164
    {
165
        $manager = $this->getEntityManager();
166
        $faker = $this->getFaker();
167
        $newsRepository = $this->getRepository(News::class);
168
        $categoryRepository = $this->getRepository(Category::class);
169
170
        foreach ($table->getHash() as $newsNode) {
171
            $news = $newsRepository->findOneByTitle($newsNode['Title']);
172
            if (!isset($news)) {
173
                $news = new News();
174
            }
175
176
            $news->setTitle($newsNode['Title']);
177
            if (isset($newsNode['Date']) && $newsNode['Date']) {
178
                $news->setDate(DateTime::createFromFormat('Y-m-d', $newsNode['Date']));
179
            }
180
            if (isset($newsNode['Category']) && $newsNode['Category']) {
181
                /** @var Category|null $category */
182
                $category = $categoryRepository->findOneBy(['title' => $newsNode['Category']]);
183
184
                if ($category === null) {
185
                    throw new InvalidArgumentException(sprintf(
186
                        'Can\'t find category by title "%s"',
187
                        $newsNode['Category']
188
                    ));
189
                }
190
191
                $news->addCategory($category);
192
            }
193
            $news->setCreatedAt($faker->dateTime());
194
            $news->setVisible($faker->boolean());
195
            $news->setCreatorEmail($faker->email());
196
197
            $manager->persist($news);
198
        }
199
200
        $manager->flush();
201
    }
202
203
    /**
204
     * @Then :className with :field :value should have :expectedCount elements in collection :collectionName
205
     */
206
    public function entityShouldHaveElementsInCollection(
207
        string $className,
208
        string $field,
209
        $value,
210
        $expectedCount,
211
        string $collectionName
212
    ) {
213
        $entity = $this->getRepository($className)->findOneBy([$field => $value]);
214
        $this->getEntityManager()->refresh($entity);
0 ignored issues
show
Bug introduced by
It seems like $entity defined by $this->getRepository($cl...rray($field => $value)) on line 213 can also be of type null; however, Doctrine\Persistence\ObjectManager::refresh() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
215
216
        expect(count($this->getEntityField($entity, $collectionName)))->toBe($expectedCount);
0 ignored issues
show
Bug introduced by
It seems like $entity defined by $this->getRepository($cl...rray($field => $value)) on line 213 can also be of type null; however, FSi\Bundle\AdminBundle\B...ntext::getEntityField() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
217
    }
218
219
    /**
220
     * @Then :className with id :id should have changed :field to :value
221
     */
222
    public function entityWithIdShouldHaveChangedField(string $className, $id, string $field, $value)
223
    {
224
        $entity = $this->getRepository($className)->find($id);
225
        $this->getEntityManager()->refresh($entity);
0 ignored issues
show
Bug introduced by
It seems like $entity defined by $this->getRepository($className)->find($id) on line 224 can also be of type null; however, Doctrine\Persistence\ObjectManager::refresh() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
226
227
        expect($this->getEntityField($entity, $field))->toBe($value);
0 ignored issues
show
Bug introduced by
It seems like $entity defined by $this->getRepository($className)->find($id) on line 224 can also be of type null; however, FSi\Bundle\AdminBundle\B...ntext::getEntityField() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
228
    }
229
230
    /**
231
     * @Then :className with id :id should not have his :field changed to :value
232
     */
233
    public function entityWithIdShouldNotHaveChangedFieldValue(string $className, $id, string $field, $value)
234
    {
235
        $entity = $this->getRepository($className)->find($id);
236
        $this->getEntityManager()->refresh($entity);
0 ignored issues
show
Bug introduced by
It seems like $entity defined by $this->getRepository($className)->find($id) on line 235 can also be of type null; however, Doctrine\Persistence\ObjectManager::refresh() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
237
238
        expect($this->getEntityField($entity, $field))->notToBe($value);
0 ignored issues
show
Bug introduced by
It seems like $entity defined by $this->getRepository($className)->find($id) on line 235 can also be of type null; however, FSi\Bundle\AdminBundle\B...ntext::getEntityField() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
239
    }
240
241
    /**
242
     * @param object $instance
243
     * @return void
244
     * @throws InvalidArgumentException
245
     */
246
    private function applyFieldFormatters($instance, ?array $formatters = null)
247
    {
248
        $className = get_class($instance);
249
        if (null === $formatters) {
250
            $formatters = $this->getFieldFormatters($className);
251
        }
252
253
        switch (true) {
254
            case $instance instanceof News:
255
                /** @var News $instance */
256
257
                /** @var string title */
258
                $title = $formatters['title']();
259
                $instance->setTitle($title);
260
261
                /** @var string $creatorEmail */
262
                $creatorEmail = $formatters['creatorEmail']();
263
                $instance->setCreatorEmail($creatorEmail);
264
265
                /** @var Category|null $categories */
266
                $categories = $formatters['categories']();
267
                array_walk(
268
                    $categories,
269
                    static function (Category $category, int $key, News $news): void {
270
                        $news->addCategory($category);
271
                    },
272
                    $instance
273
                );
274
275
                /** @var bool $visible */
276
                $visible = $formatters['visible']();
277
                $instance->setVisible($visible);
278
279
                /** @var string|null $photoKey */
280
                $photoKey = $formatters['photoKey']();
281
                $instance->setPhotoKey($photoKey);
282
                break;
283
            case $instance instanceof Person:
284
                /** @var Person $instance */
285
286
                /** @var string $email */
287
                $email = $formatters['email']();
288
                $instance->setEmail($email);
289
                break;
290
            case $instance instanceof Subscriber:
291
                /** @var Subscriber $instance */
292
293
                /** @var string $email */
294
                $email = $formatters['email']();
295
                $instance->setEmail($email);
296
297
                /** @var bool $active */
298
                $active = $formatters['active']();
299
                $instance->setActive($active);
300
                break;
301
            case $instance instanceof Category:
302
                /** @var string title */
303
                $title = $formatters['title']();
304
                $instance->setTitle($title);
305
                break;
306
            default:
307
                throw new InvalidArgumentException(sprintf(
308
                    'Cannot find any column formatters for class "%s',
309
                    $className
310
                ));
311
        }
312
    }
313
314
    private function getFieldFormatters(string $className): array
315
    {
316
        $faker = $this->getFaker();
317
        $formatters = [
318
            News::class => [
319
                'title' => function () use ($faker) {
320
                    return $faker->title;
321
                },
322
                'creatorEmail' => function () use ($faker): string {
323
                    return $faker->email();
324
                },
325
                'categories' => function () use ($faker): array {
326
                    /** @var array<Category> $categories */
327
                    $categories = $this->getRepository(Category::class)->findAll();
328
                    if (0 !== count($categories)) {
329
                        /** @var Category $randomCategory */
330
                        $randomCategory = $faker->randomElement($categories);
331
                        $categories = [$randomCategory];
332
                    }
333
334
                    return $categories;
335
                },
336
                'photoKey' => function (): ?string {
337
                    return null;
338
                },
339
                'visible' => function (): bool {
340
                    return true;
341
                }
342
            ],
343
            Person::class => [
344
                'email' => function () use ($faker): string {
345
                    return $faker->email();
346
                }
347
            ],
348
            Subscriber::class => [
349
                'email' => function () use ($faker): string {
350
                    return $faker->email();
351
                },
352
                'active' => function (): bool {
353
                    return true;
354
                }
355
            ],
356
            Category::class => [
357
                'title' => function () use ($faker): string {
358
                    return $faker->title;
359
                }
360
            ]
361
        ];
362
363
        if (false === array_key_exists($className, $formatters)) {
364
            throw new RuntimeException("No formatters for class \"{$className}\"");
365
        }
366
367
        return $formatters[$className];
368
    }
369
370
    /**
371
     * @param object $instance
372
     * @return void
373
     * @throws InvalidArgumentException
374
     */
375
    private function applyEntityModifiers($instance): void
376
    {
377
        $faker = $this->getFaker();
378
        switch (true) {
379
            case $instance instanceof News:
380
                $instance->setTitle($faker->title);
381
                $instance->setCreatedAt(new DateTime());
382
                $tag = new Tag();
383
                $tag->setName($faker->sentence());
384
                $tag->setNews($instance);
385
                $instance->setTags([$tag]);
386
                break;
387
            case $instance instanceof Subscriber:
388
                $instance->setCreatedAt(new DateTime());
389
                break;
390
            case $instance instanceof Person:
391
            case $instance instanceof Category:
392
                break;
393
            default:
394
                throw new InvalidArgumentException(sprintf(
395
                    'Cannot find any modifiers for class "%s',
396
                    get_class($instance)
397
                ));
398
        }
399
    }
400
401
    /**
402
     * @param object $entity
403
     * @param string $field
404
     * @return mixed
405
     */
406
    private function getEntityField($entity, string $field)
407
    {
408
        if (null === $this->propertyAccessor) {
409
            $this->propertyAccessor = PropertyAccess::createPropertyAccessor();
410
        }
411
412
        return $this->propertyAccessor->getValue($entity, strtolower($field));
413
    }
414
}
415