Completed
Push — master ( 0f74a6...eca3e7 )
by Kamil
20:39
created

DefaultContext::getColumnIndex()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 20
rs 9.2
cc 4
eloc 10
nc 4
nop 2
1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sylius\Bundle\ResourceBundle\Behat;
13
14
use Behat\Behat\Context\Context;
15
use Behat\Mink\Element\NodeElement;
16
use Behat\Mink\Exception\ElementNotFoundException;
17
use Behat\MinkExtension\Context\RawMinkContext;
18
use Behat\Symfony2Extension\Context\KernelAwareContext;
19
use Doctrine\Common\Collections\ArrayCollection;
20
use Doctrine\Common\Persistence\ObjectManager;
21
use Faker\Factory as FakerFactory;
22
use Faker\Generator;
23
use Sylius\Component\Resource\Factory\FactoryInterface;
24
use Sylius\Component\Resource\Repository\RepositoryInterface;
25
use Symfony\Component\DependencyInjection\ContainerInterface;
26
use Symfony\Component\HttpKernel\KernelInterface;
27
use Symfony\Component\Intl\Intl;
28
use Symfony\Component\Routing\RouterInterface;
29
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
30
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
31
use Symfony\Component\Security\Core\User\UserInterface;
32
33
abstract class DefaultContext extends RawMinkContext implements Context, KernelAwareContext
34
{
35
    /**
36
     * @var string
37
     */
38
    protected $applicationName = 'sylius';
39
40
    /**
41
     * @var Generator
42
     */
43
    protected $faker;
44
45
    /**
46
     * @var array
47
     */
48
    protected $actions = [
49
        'viewing' => 'show',
50
        'creation' => 'create',
51
        'editing' => 'update',
52
        'building' => 'build',
53
        'customization' => 'customize',
54
    ];
55
56
    /**
57
     * @var KernelInterface
58
     */
59
    private $kernel;
60
61
    /**
62
     * @var KernelInterface
63
     */
64
    private static $sharedKernel;
65
66
    public function __construct($applicationName = null)
67
    {
68
        \Locale::setDefault('en');
69
70
        $this->faker = FakerFactory::create();
71
72
        if (null !== $applicationName) {
73
            $this->applicationName = $applicationName;
74
        }
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80
    public function setKernel(KernelInterface $kernel)
81
    {
82
        $this->kernel = $kernel;
83
84
        if (null === self::$sharedKernel) {
85
            self::$sharedKernel = clone $kernel;
86
            self::$sharedKernel->boot();
87
        }
88
    }
89
90
    /**
91
     * @param string $type
92
     * @param string $name
93
     *
94
     * @return object
95
     */
96
    protected function findOneByName($type, $name)
97
    {
98
        $resource = $this->getRepository($type)->findOneByName(trim($name));
99
100
        if (null === $resource) {
101
            throw new \InvalidArgumentException(
102
                sprintf('%s with name "%s" was not found.', str_replace('_', ' ', ucfirst($type)), $name)
103
            );
104
        }
105
106
        return $resource;
107
    }
108
109
    /**
110
     * @param string $type
111
     * @param array  $criteria
112
     *
113
     * @return object
114
     *
115
     * @throws \InvalidArgumentException
116
     */
117
    protected function findOneBy($type, array $criteria)
118
    {
119
        $resource = $this
120
            ->getRepository($type)
121
            ->findOneBy($criteria)
122
        ;
123
124
        if (null === $resource) {
125
            throw new \InvalidArgumentException(
126
                sprintf('%s for criteria "%s" was not found.', str_replace('_', ' ', ucfirst($type)), serialize($criteria))
127
            );
128
        }
129
130
        return $resource;
131
    }
132
133
    /**
134
     * @param string $resourceName
135
     *
136
     * @return RepositoryInterface
137
     */
138
    protected function getRepository($resourceName)
139
    {
140
        return $this->getService($this->applicationName.'.repository.'.$resourceName);
141
    }
142
143
    /**
144
     * @param string $resourceName
145
     *
146
     * @return FactoryInterface
147
     */
148
    protected function getFactory($resourceName)
149
    {
150
        return $this->getService($this->applicationName.'.factory.'.$resourceName);
151
    }
152
153
    /**
154
     * @return ObjectManager
155
     */
156
    protected function getEntityManager()
157
    {
158
        return $this->getService('doctrine')->getManager();
159
    }
160
161
    /**
162
     * @return ContainerInterface
163
     */
164
    protected function getContainer()
165
    {
166
        return $this->kernel->getContainer();
167
    }
168
169
    /**
170
     * @param string $id
171
     *
172
     * @return object
173
     */
174
    protected function getService($id)
175
    {
176
        return $this->getContainer()->get($id);
177
    }
178
179
    /**
180
     * Configuration converter.
181
     *
182
     * @param string $configurationString
183
     *
184
     * @return array
185
     */
186
    protected function getConfiguration($configurationString)
187
    {
188
        $configuration = [];
189
        $list = explode(',', $configurationString);
190
191
        foreach ($list as $parameter) {
192
            list($key, $value) = explode(':', $parameter, 2);
193
            $key = strtolower(trim(str_replace(' ', '_', $key)));
194
195
            switch ($key) {
196
                case 'country':
197
                    $countryCode = $this->getCountryCodeByEnglishCountryName(trim($value));
198
199
                    $configuration[$key] = $this->getRepository('country')->findOneBy(['code' => $countryCode])->getId();
200
                    break;
201
202
                case 'taxons':
203
                    $configuration[$key] = [$this->getRepository('taxon')->findOneByName(trim($value))->getCode()];
204
                    break;
205
206
                case 'variant':
207
                    $configuration[$key] = $this->getRepository('product')->findOneByName($value)->getMasterVariant()->getId();
208
                    break;
209
210
                case 'amount':
211
                    $configuration[$key] = (int) $value;
212
                    break;
213
214
                case 'nth':
215
                    $configuration[$key] = (int) $value;
216
                    break;
217
218
                default:
219
                    $configuration[$key] = trim($value);
220
                    break;
221
            }
222
        }
223
224
        return $configuration;
225
    }
226
227
    /**
228
     * Generate page url.
229
     * This method uses simple convention where page argument is prefixed
230
     * with the application name and used as route name passed to router generate method.
231
     *
232
     * @param object|string $page
233
     * @param array         $parameters
234
     *
235
     * @return string
236
     */
237
    protected function generatePageUrl($page, array $parameters = [])
238
    {
239
        if (is_object($page)) {
240
            return $this->generateUrl($page, $parameters);
241
        }
242
243
        $route = str_replace(' ', '_', trim($page));
244
        $routes = $this->getRouter()->getRouteCollection();
245
246
        if (null === $routes->get($route)) {
247
            $route = $this->applicationName.'_'.$route;
248
        }
249
250
        if (null === $routes->get($route)) {
251
            $route = str_replace($this->applicationName.'_', $this->applicationName.'_backend_', $route);
252
        }
253
254
        $route = str_replace(array_keys($this->actions), array_values($this->actions), $route);
255
        $route = str_replace(' ', '_', $route);
256
257
        return $this->generateUrl($route, $parameters);
258
    }
259
260
    /**
261
     * Get current user instance.
262
     *
263
     * @return UserInterface|null
264
     *
265
     * @throws \Exception
266
     */
267
    protected function getUser()
268
    {
269
        $token = $this->getTokenStorage()->getToken();
270
271
        if (null === $token) {
272
            throw new \Exception('No token found in security context.');
273
        }
274
275
        return $token->getUser();
276
    }
277
278
    /**
279
     * @return TokenStorageInterface
280
     */
281
    protected function getTokenStorage()
282
    {
283
        return $this->getContainer()->get('security.token_storage');
284
    }
285
286
    /**
287
     * @return AuthorizationCheckerInterface
288
     */
289
    protected function getAuthorizationChecker()
290
    {
291
        return $this->getContainer()->get('security.authorization_checker');
292
    }
293
294
    /**
295
     * @param string  $route
296
     * @param array   $parameters
297
     * @param bool $absolute
298
     *
299
     * @return string
300
     */
301
    protected function generateUrl($route, array $parameters = [], $absolute = false)
302
    {
303
        return $this->locatePath($this->getRouter()->generate($route, $parameters, $absolute));
304
    }
305
306
    /**
307
     * Presses button with specified id|name|title|alt|value.
308
     *
309
     * @param string $button
310
     *
311
     * @throws ElementNotFoundException
312
     */
313
    protected function pressButton($button)
314
    {
315
        $this->getSession()->getPage()->pressButton($this->fixStepArgument($button));
316
    }
317
318
    /**
319
     * Clicks link with specified id|title|alt|text.
320
     *
321
     * @param string $link
322
     *
323
     * @throws ElementNotFoundException
324
     */
325
    protected function clickLink($link)
326
    {
327
        $this->getSession()->getPage()->clickLink($this->fixStepArgument($link));
328
    }
329
330
    /**
331
     * Fills in form field with specified id|name|label|value.
332
     *
333
     * @param string $field
334
     * @param string $value
335
     *
336
     * @throws ElementNotFoundException
337
     */
338
    protected function fillField($field, $value)
339
    {
340
        $this->getSession()->getPage()->fillField(
341
            $this->fixStepArgument($field),
342
            $this->fixStepArgument($value)
343
        );
344
    }
345
346
    /**
347
     * Selects option in select field with specified id|name|label|value.
348
     *
349
     * @param string $select
350
     * @param string $option
351
     *
352
     * @throws ElementNotFoundException
353
     */
354
    protected function selectOption($select, $option)
355
    {
356
        $this->getSession()->getPage()->selectFieldOption(
357
            $this->fixStepArgument($select),
358
            $this->fixStepArgument($option)
359
        );
360
    }
361
362
    /**
363
     * Returns fixed step argument (with \\" replaced back to ").
364
     *
365
     * @param string $argument
366
     *
367
     * @return string
368
     */
369
    protected function fixStepArgument($argument)
370
    {
371
        return str_replace('\\"', '"', $argument);
372
    }
373
374
    /**
375
     * @param NodeElement $table
376
     * @param string $columnName
377
     *
378
     * @return int
379
     *
380
     * @throws \Exception If column was not found
381
     */
382
    protected function getColumnIndex(NodeElement $table, $columnName)
383
    {
384
        $rows = $table->findAll('css', 'tr');
385
386
        if (!isset($rows[0])) {
387
            throw new \Exception('There are no rows!');
388
        }
389
390
        /** @var NodeElement $firstRow */
391
        $firstRow = $rows[0];
392
        $columns = $firstRow->findAll('css', 'th,td');
393
        foreach ($columns as $index => $column) {
394
            /** @var NodeElement $column */
395
            if (0 === stripos($column->getText(), $columnName)) {
396
                return $index;
397
            }
398
        }
399
400
        throw new \Exception(sprintf('Column with name "%s" not found!', $columnName));
401
    }
402
403
    /**
404
     * @param NodeElement $table
405
     * @param array $fields
406
     *
407
     * @return NodeElement|null
408
     *
409
     * @throws \Exception If column was not found
410
     */
411
    protected function getRowWithFields(NodeElement $table, array $fields)
412
    {
413
        $foundRows = $this->getRowsWithFields($table, $fields, true);
414
415
        if (empty($foundRows)) {
416
            return null;
417
        }
418
419
        return current($foundRows);
420
    }
421
422
    /**
423
     * @param NodeElement $table
424
     * @param array $fields
425
     * @param bool $onlyFirstOccurence
426
     *
427
     * @return NodeElement[]
428
     *
429
     * @throws \Exception If columns or rows were not found
430
     */
431
    protected function getRowsWithFields(NodeElement $table, array $fields, $onlyFirstOccurence = false)
432
    {
433
        $rows = $table->findAll('css', 'tr');
434
435
        if (!isset($rows[0])) {
436
            throw new \Exception('There are no rows!');
437
        }
438
439
        $fields = $this->replaceColumnNamesWithColumnIds($table, $fields);
440
441
        $foundRows = [];
442
443
        /** @var NodeElement[] $rows */
444
        $rows = $table->findAll('css', 'tr');
445
        foreach ($rows as $row) {
446
            $found = true;
447
448
            /** @var NodeElement[] $columns */
449
            $columns = $row->findAll('css', 'th,td');
450
            foreach ($fields as $index => $searchedValue) {
451
                if (!isset($columns[$index])) {
452
                    throw new \InvalidArgumentException(sprintf('There is no column with index %d', $index));
453
                }
454
455
                $containing = false;
456
                $searchedValue = trim($searchedValue);
457
                if (0 === strpos($searchedValue, '%') && (strlen($searchedValue) - 1) === strrpos($searchedValue, '%')) {
458
                    $searchedValue = substr($searchedValue, 1, strlen($searchedValue) - 2);
459
                    $containing = true;
460
                }
461
462
                $position = stripos(trim($columns[$index]->getText()), $searchedValue);
463
                if (($containing && false === $position) || (!$containing && 0 !== $position)) {
464
                    $found = false;
465
466
                    break;
467
                }
468
            }
469
470
            if ($found) {
471
                $foundRows[] = $row;
472
473
                if ($onlyFirstOccurence) {
474
                    break;
475
                }
476
            }
477
        }
478
479
        return $foundRows;
480
    }
481
482
    /**
483
     * @param NodeElement $table
484
     * @param string[] $fields
485
     *
486
     * @return string[]
487
     *
488
     * @throws \Exception
489
     */
490
    protected function replaceColumnNamesWithColumnIds(NodeElement $table, array $fields)
491
    {
492
        $replacedFields = [];
493
        foreach ($fields as $columnName => $expectedValue) {
494
            $columnIndex = $this->getColumnIndex($table, $columnName);
495
496
            $replacedFields[$columnIndex] = $expectedValue;
497
        }
498
499
        return $replacedFields;
500
    }
501
502
    /**
503
     * @param callable $callback
504
     * @param int $limit
505
     * @param int $delay In miliseconds
506
     *
507
     * @return mixed
508
     *
509
     * @throws \RuntimeException If timeout was reached
510
     */
511
    protected function waitFor(callable $callback, $limit = 30, $delay = 100)
512
    {
513
        for ($i = 0; $i < $limit; ++$i) {
514
            $payload = $callback();
515
516
            if (!empty($payload)) {
517
                return $payload;
518
            }
519
520
            usleep($delay * 1000);
521
        }
522
523
        throw new \RuntimeException(sprintf('Timeout reached (%f seconds)!', round($limit * $delay / 1000, 1)));
524
    }
525
526
    /**
527
     * @param string $name
528
     *
529
     * @return string
530
     *
531
     * @throws \InvalidArgumentException If name is not found in country code registry.
532
     */
533
    protected function getCountryCodeByEnglishCountryName($name)
534
    {
535
        $names = Intl::getRegionBundle()->getCountryNames('en');
536
        $countryCode = array_search(trim($name), $names);
537
538
        if (null === $countryCode) {
539
            throw new \InvalidArgumentException(sprintf(
540
                'Country "%s" not found! Available names: %s.', $name, implode(', ', $names)
541
            ));
542
        }
543
544
        return $countryCode;
545
    }
546
547
    /**
548
     * @param string $name
549
     *
550
     * @return string
551
     *
552
     * @throws \InvalidArgumentException If name is not found in locale code registry.
553
     */
554
    protected function getLocaleCodeByEnglishLocaleName($name)
555
    {
556
        $localeNameConverter = $this->getService('sylius.converter.locale_name');
557
558
        return $localeNameConverter->convertToCode($name);
559
    }
560
561
    /**
562
     * @return RouterInterface
563
     */
564
    protected function getRouter()
565
    {
566
        return $this->getSharedService('router');
567
    }
568
569
    /**
570
     * @return KernelInterface
571
     */
572
    protected function getKernel()
573
    {
574
        return $this->kernel;
575
    }
576
577
    /**
578
     * @return KernelInterface
579
     */
580
    protected function getSharedKernel()
581
    {
582
        return self::$sharedKernel;
583
    }
584
585
    /**
586
     * @param string $id
587
     *
588
     * @return object
589
     */
590
    protected function getSharedService($id)
591
    {
592
        return self::$sharedKernel->getContainer()->get($id);
593
    }
594
}
595