Completed
Push — master ( 0eab77...b2f729 )
by Paul
05:35
created

VictoireContext::fillSearchField()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.2
c 0
b 0
f 0
cc 4
eloc 13
nc 4
nop 3
1
<?php
2
3
namespace Victoire\Tests\Features\Context;
4
5
use Behat\Behat\Hook\Scope\AfterStepScope;
6
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
7
use Behat\Mink\Driver\Selenium2Driver;
8
use Behat\Mink\Element\DocumentElement;
9
use Behat\Mink\Element\Element;
10
use Behat\Mink\Session;
11
use Behat\Symfony2Extension\Context\KernelDictionary;
12
use Knp\FriendlyContexts\Context\RawMinkContext;
13
14
/**
15
 * This class gives some usefull methods for Victoire navigation.
16
 *
17
 * @property MinkContext minkContext
18
 */
19
class VictoireContext extends RawMinkContext
20
{
21
    use KernelDictionary;
22
    protected $minkContext;
23
24
    /**
25
     * @BeforeScenario
26
     *
27
     * @param BeforeScenarioScope $scope
28
     */
29
    public function gatherContexts(BeforeScenarioScope $scope)
30
    {
31
        $environment = $scope->getEnvironment();
32
        $this->minkContext = $environment->getContext('Victoire\Tests\Features\Context\MinkContext');
33
    }
34
35
    /**
36
     * @BeforeScenario
37
     *
38
     * @param BeforeScenarioScope $scope
39
     */
40
    public function resetViewsReference(BeforeScenarioScope $scope)
0 ignored issues
show
Unused Code introduced by
The parameter $scope is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
41
    {
42
        $viewsReferences = $this->getContainer()->get('victoire_core.view_helper')->buildViewsReferences();
43
        $this->getContainer()->get('victoire_view_reference.manager')->saveReferences($viewsReferences);
44
    }
45
46
    /**
47
     * @AfterStep
48
     *
49
     * @param AfterStepScope $scope
50
     *
51
     * @throws \Exception
52
     */
53
    public function lookForJSErrors(AfterStepScope $scope)
54
    {
55
        /* @var Session $session */
56
        $session = $this->getSession();
57
58
        if (!($session->getDriver() instanceof Selenium2Driver)) {
59
            return;
60
        }
61
62
        try {
63
            $errors = $session->evaluateScript('window.jsErrors');
64
            $session->evaluateScript('window.jsErrors = []');
65
        } catch (\Exception $e) {
66
            throw $e;
67
        }
68
        if (!$errors || empty($errors)) {
69
            return;
70
        }
71
        $file = sprintf('%s:%d', $scope->getFeature()->getFile(), $scope->getStep()->getLine());
72
        $message = sprintf('Found %d javascript error%s', count($errors), count($errors) > 0 ? 's' : '');
73
        echo '-------------------------------------------------------------'.PHP_EOL;
74
        echo $file.PHP_EOL;
75
        echo $message.PHP_EOL;
76
        echo '-------------------------------------------------------------'.PHP_EOL;
77
        foreach ($errors as $index => $error) {
0 ignored issues
show
Bug introduced by
The expression $errors of type string is not traversable.
Loading history...
78
            echo sprintf('   #%d: %s', $index, $error).PHP_EOL;
79
        }
80
    }
81
82
    /**
83
     * @Given I am logged in as :email
84
     */
85
    public function iAmLoggedInAsUser($email)
86
    {
87
        $this->minkContext->visit('/login');
88
        $this->minkContext->fillField('username', $email);
89
        $this->minkContext->fillField('password', 'test');
90
        $this->minkContext->pressButton('_submit');
91
    }
92
93
    /**
94
     * @Given I login as visitor
95
     */
96
    public function iLoginAsVisitor()
97
    {
98
        $this->getSession()->getDriver()->stop();
99
        $baseUrl = $this->minkContext->getMinkParameter('base_url');
100
        $url = str_replace('[email protected]:test', '[email protected]:test', $baseUrl);
101
        $this->minkContext->setMinkParameter('base_url', $url);
102
    }
103
104
    /**
105
     * @Given /^I visit homepage through domain "([^"]*)"$/
106
     */
107
    public function ivisitHomepageThroughDomain($domain)
108
    {
109
        $this->getSession()->getDriver()->stop();
110
        $url = sprintf('http://[email protected]:test@%s:8000/app_domain.php', $domain);
111
        $this->minkContext->setMinkParameter('base_url', $url);
112
        $this->minkContext->visitPath('/');
113
    }
114
115
    /**
116
     * @Then /^I fill in wysiwyg with "([^"]*)"$/
117
     */
118
    public function iFillInWysiwygOnFieldWith($arg)
119
    {
120
        $js = 'CKEDITOR.instances.victoire_widget_form_ckeditor_content.setData("'.$arg.'");';
121
        $this->getSession()->executeScript($js);
122
    }
123
124
    /**
125
     * @Then /^I select "([^"]*)" from the "([^"]*)" select of "([^"]*)" slot$/
126
     */
127
    public function iSelectFromTheSelectOfSlot($widget, $nth, $slot)
128
    {
129
        $slot = $this->getSession()->getPage()->find('xpath', 'descendant-or-self::*[@id="vic-slot-'.$slot.'"]');
130
        $selects = $slot->findAll('css', 'select[role="menu"]');
131
        $selects[$nth - 1]->selectOption(str_replace('\\"', '"', $widget));
132
    }
133
134
    /**
135
     * @Then /^I switch to "([^"]*)" mode$/
136
     */
137 View Code Duplication
    public function iSwitchToMode($mode)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
138
    {
139
        $element = $this->findOrRetry($this->getSession()->getPage(), 'xpath', 'descendant-or-self::*[@for="mode-switcher--'.$mode.'"]');
140
141
        if (null === $element) {
142
            $message = sprintf('Element not found in the page after 10 seconds"');
143
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
144
        }
145
        $element->click();
146
    }
147
148
    /**
149
     * @Then /^I (open|close|toggle) the hamburger menu$/
150
     */
151 View Code Duplication
    public function iOpenTheHamburgerMenu()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
152
    {
153
        $element = $this->findOrRetry(
154
            $this->getSession()->getPage(),
155
            'xpath',
156
            'descendant-or-self::*[@id="vic-menu-leftnavbar-trigger"]'
157
        );
158
159
        if (null === $element) {
160
            $message = sprintf('Element not found in the page after 10 seconds"');
161
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
162
        }
163
        $element->click();
164
    }
165
166
    /**
167
     * @When I open the widget mode drop for entity :entity
168
     */
169 View Code Duplication
    public function iOpenTheWidgetModeDrop($entity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
170
    {
171
        $element = $this->findOrRetry(
172
            $this->getSession()->getPage(),
173
            'css',
174
            '[id^="picker-'.strtolower($entity).'"] .v-mode-trigger'
175
        );
176
        if (null === $element) {
177
            $message = sprintf('Element not found in the page after 10 seconds"');
178
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
179
        }
180
        $element->click();
181
    }
182
183
    /**
184
     * @When I open the widget style tab :key
185
     */
186
    public function iOpenTheWidgetStyleTab($key)
187
    {
188
        $element = $this->findOrRetry(
189
            $this->getSession()->getPage(),
190
            'css',
191
            '[title="style-'.$key.'"]'
192
        );
193
        if (null === $element) {
194
            $message = sprintf('Element not found in the page after 10 seconds"');
195
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
196
        }
197
        $element->click();
198
    }
199
200
    /**
201
     * @When I follow the float action button
202
     */
203 View Code Duplication
    public function iFollowTheFloatAction()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
204
    {
205
        $element = $this->findOrRetry(
206
            $this->getSession()->getPage(),
207
            'css',
208
            '#v-float-container [data-flag="v-drop v-drop-fab"]'
209
        );
210
        if (null === $element) {
211
            $message = sprintf('Element not found in the page after 10 seconds"');
212
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
213
        }
214
        $element->click();
215
    }
216
217
    /**
218
     * @When I open the widget quantum collapse for entity :entity
219
     */
220 View Code Duplication
    public function iOpenTheWidgetQuantumCollapse($entity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
221
    {
222
        $element = $this->findOrRetry(
223
            $this->getSession()->getPage(),
224
            'css',
225
            '[id^="picker-'.strtolower($entity).'"][data-state="visible"] [id^="picker-'.strtolower($entity).'"][data-state="visible"] .v-widget-form__quantum-btn'
226
        );
227
228
        if (null === $element) {
229
            $message = sprintf('Element not found in the page after 10 seconds"');
230
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
231
        }
232
        $element->click();
233
    }
234
235
    /**
236
     * @When I open the widget quantum collapse when static
237
     */
238 View Code Duplication
    public function iOpenTheWidgetQuantumCollapseWhenStatic()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
239
    {
240
        $element = $this->findOrRetry(
241
            $this->getSession()->getPage(),
242
            'css',
243
            '[data-state="visible"] [id^="picker-static"] .v-widget-form__quantum-btn'
244
        );
245
246
        if (null === $element) {
247
            $message = sprintf('Element not found in the page after 10 seconds"');
248
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
249
        }
250
        $element->click();
251
    }
252
253
    /**
254
     * @Then /^I open the settings menu$/
255
     */
256 View Code Duplication
    public function iOpenTheSettingsMenu()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
257
    {
258
        $element = $this->findOrRetry(
259
            $this->getSession()->getPage(),
260
            'xpath',
261
            'descendant-or-self::*[@id="v-settings-link"]'
262
        );
263
264
        if (null === $element) {
265
            $message = sprintf('Element not found in the page after 10 seconds"');
266
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
267
        }
268
        $element->click();
269
    }
270
271
    /**
272
     * @Then /^I open the additionals menu drop$/
273
     */
274 View Code Duplication
    public function iOpenTheAdditionalsMenuDrop()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
275
    {
276
        $element = $this->findOrRetry(
277
            $this->getSession()->getPage(),
278
            'xpath',
279
            'descendant-or-self::*[@id="v-additionals-drop"]'
280
        );
281
282
        if (null === $element) {
283
            $message = sprintf('Element not found in the page after 10 seconds"');
284
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
285
        }
286
        $element->click();
287
    }
288
289
    /**
290
     * @When I follow the tab :name
291
     */
292
    public function iFollowTheTab($name)
293
    {
294
        $element = $this->findOrRetry($this->getSession()->getPage(), 'xpath', sprintf('descendant-or-self::a[contains(@class, "v-tabs-nav__anchor") and normalize-space(text()) = "%s"]', $name));
295
296
        // @TODO When the new styleguide is completly integrated, remove.
297
        if (null === $element) {
298
            $element = $this->findOrRetry($this->getSession()->getPage(), 'xpath', sprintf('descendant-or-self::a[@data-toggle="vic-tab" and normalize-space(text()) = "%s"]', $name));
299
        }
300
301
        if (null === $element) {
302
            $message = sprintf('Element not found in the page after 10 seconds"');
303
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
304
        }
305
        $element->click();
306
    }
307
308
    /**
309
     * @When I follow the drop trigger :name
310
     */
311 View Code Duplication
    public function iFollowTheDropTrigger($name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
312
    {
313
        $element = $this->findOrRetry($this->getSession()->getPage(), 'xpath', sprintf('descendant-or-self::a[@data-flag*="v-drop" and normalize-space(text()) = "%s"]', $name));
314
315
        if (null === $element) {
316
            $message = sprintf('Element not found in the page after 10 seconds"');
317
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
318
        }
319
        $element->click();
320
    }
321
322
    /**
323
     * @When I follow the drop anchor :name
324
     */
325
    public function iFollowTheDropAnchor($name)
326
    {
327
        $page = $this->getSession()->getPage();
328
        $elements = $page->findAll('xpath', sprintf('descendant-or-self::a[contains(@class, "v-drop__anchor") and normalize-space(text()) = "%s"]', $name));
329
330
        if (count($elements) < 1) {
331
            $message = sprintf('Element not found in the page after 10 seconds"');
332
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
333
        }
334
335
        foreach ($elements as $element) {
336
            if ($element->getText() === $name) {
337
                $element->click();
338
            }
339
        }
340
    }
341
342
    /**
343
     * @Then /^I submit the widget$/
344
     * @Then /^I submit the modal$/
345
     */
346
    public function iSubmitTheWidget()
347
    {
348
        $element = $this->getSession()->getPage()->find('xpath', 'descendant-or-self::a[@data-modal="create"]');
349
350
        if (!$element) {
351
            $element = $this->getSession()->getPage()->find('xpath', 'descendant-or-self::a[@data-modal="update"]');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $element is correct as $this->getSession()->get...@data-modal="update"]') (which targets Behat\Mink\Element\Element::find()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
352
        }
353
        $element->click();
354
        $this->getSession()->wait(2000);
355
    }
356
357
    /**
358
     * @Given /^I edit an "([^"]*)" widget$/
359
     * @Given /^I edit the "([^"]*)" widget$/
360
     */
361
    public function iEditTheWidget($widgetType)
362
    {
363
        $selector = sprintf('.v-widget--%s > a.v-widget__overlay', strtolower($widgetType));
364
        $session = $this->getSession(); // get the mink session
365
        $element = $this->findOrRetry($session->getPage(), 'css', $selector);
366
367
        // errors must not pass silently
368
        if (null === $element) {
369
            throw new \InvalidArgumentException(sprintf('Could not evaluate CSS selector: "%s"', $selector));
370
        }
371
372
        // ok, let's hover it
373
        $element->mouseOver();
374
        $element->click();
375
    }
376
377
    /**
378
     * @Then /^"([^"]*)" should precede "([^"]*)"$/
379
     */
380
    public function shouldPrecedeForTheQuery($textBefore, $textAfter)
381
    {
382
        $element = $this->getSession()->getPage()->find(
383
            'xpath',
384
            sprintf('//*[normalize-space(text()) = "%s"][preceding::*[normalize-space(text()) = "%s"]]',
385
                $textAfter,
386
                $textBefore
387
            )
388
        );
389
        if (null === $element) {
390
            $message = sprintf('"%s" does not preceed "%s"', $textBefore, $textAfter);
391
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
392
        }
393
    }
394
395
    /**
396
     * @When /^I select the option "(?P<option>[^"]*)" in the dropdown "(?P<dropdown>[^"]*)"$/
397
     */
398
    public function iSelectTheOptionInTheDropdown($option, $dropdown)
399
    {
400
        $link = $this->getSession()->getPage()->find('css', sprintf('a.vic-dropdown-toggle[title="%s"]', $dropdown));
401
        $link->click();
402
        $optionButton = $this->getSession()->getPage()->find('css', sprintf('ul[aria-labelledby="%sDropdownMenu"] > li > a[title="%s"]', $dropdown, $option));
403
        $optionButton->click();
404
    }
405
406
    /**
407
     * @Then /^I attach image with id "(\d+)" to victoire field "(.+)"$/
408
     */
409
    public function attachImageToVictoireScript($imageId, $fieldId)
410
    {
411
        $script = sprintf('$("#%s input").val(%d)', $fieldId, $imageId);
412
        $this->getSession()->executeScript($script);
413
    }
414
415
    /**
416
     * @Then I should find css element :element with selector :selector and value :value
417
     */
418
    public function iShouldFindCssWithSelectorAndValue($element, $selector, $value)
419
    {
420
        $css = sprintf('%s[%s="%s"]', $element, $selector, $value);
421
        $session = $this->getSession();
422
        $element = $this->findOrRetry($session->getPage(), 'css', $css);
423
424
        if (null === $element) {
425
            $message = sprintf('Element not found. String generate: %s[%s="%s"]', $element, $selector, $value);
426
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
427
        }
428
    }
429
430
    /**
431
     * @Then I should see disable drop anchor :name
432
     */
433 View Code Duplication
    public function iShouldSeeDisableDropAnchor($name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
434
    {
435
        $element = $this->findOrRetry($this->getSession()->getPage(), 'xpath', sprintf('descendant-or-self::*[contains(@class, \'v-drop__anchor--disabled\') and normalize-space(.) = "%s"]', $name));
436
437
        if (null === $element) {
438
            $message = sprintf('Element not found in the page after 10 seconds"');
439
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
440
        }
441
    }
442
443
    /**
444
     * @Then I should see disable tab :name
445
     */
446 View Code Duplication
    public function iShouldSeeDisableTab($name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
447
    {
448
        $element = $this->findOrRetry($this->getSession()->getPage(), 'xpath', sprintf('descendant-or-self::li[@class="vic-disable" and normalize-space(.) = "%s"]', $name));
449
450
        if (null === $element) {
451
            $message = sprintf('Element not found in the page after 10 seconds"');
452
            throw new \Behat\Mink\Exception\ResponseTextException($message, $this->getSession());
453
        }
454
    }
455
456
    /**
457
     * @Then /^I move the widgetMap "(.+)" "(.+)" the widgetMap "(.*)"$/
458
     */
459
    public function iMoveWidgetUnder($widgetMapMoved, $position, $widgetMapMovedTo)
460
    {
461
        if (!$widgetMapMovedTo) {
462
            $widgetMapMovedTo = 'null';
463
        }
464
        $js = 'updateWidgetPosition({"parentWidgetMap": '.$widgetMapMovedTo.', "slot": "main_content", "position": "'.$position.'", "widgetMap": '.$widgetMapMoved.'})';
465
466
        $this->getSession()->executeScript($js);
467
    }
468
469
    /**
470
     * @When /^I rename quantum "(.+)" with "(.+)"$/
471
     */
472
    public function iRenameQuantumWith($quantumPosition, $name)
473
    {
474
        $session = $this->getSession();
475
476
        $pencilSelector = sprintf('descendant-or-self::ul[contains(@class, \'vic-quantum-nav\')]/li[%s]/a/i[contains(@class, \'fa-pencil\')]', $quantumPosition);
477
        $pencil = $this->findOrRetry($session->getPage(), 'xpath', $pencilSelector);
478
        $pencil->click();
479
480
        $input = $this->findOrRetry($session->getPage(), 'css', '.quantum-edit-field');
481
        $input->setValue($name);
482
483
        //Click outside
484
        $list = $this->findOrRetry($session->getPage(), 'css', '.vic-quantum-nav');
485
        $list->click();
486
    }
487
488
    /**
489
     * @When /^I select quantum "(.+)"$/
490
     */
491
    public function iSelectQuantum($quantumName)
492
    {
493
        $session = $this->getSession();
494
495
        $quantumSelector = sprintf('descendant-or-self::a[contains(@class, \'v-btn--quantum\') and normalize-space(.) = "%s"]', $quantumName);
496
        $quantum = $this->findOrRetry($session->getPage(), 'xpath', $quantumSelector);
497
        $quantum->click();
498
    }
499
500
    /**
501
     * @When /^I create a new quantum$/
502
     */
503
    public function iCreateANewQuantum()
504
    {
505
        $session = $this->getSession();
506
507
        $element = $this->findOrRetry($session->getPage(), 'css', '#widget-new-tab');
508
        $element->click();
509
    }
510
511
    /**
512
     * Try to find value in element and retry for a given time.
513
     *
514
     * @param Element $element
515
     * @param string  $selectorType xpath|css
516
     * @param string  $value
517
     * @param int     $timeout
518
     *
519
     * @return \Behat\Mink\Element\NodeElement|mixed|null|void
520
     */
521
    protected function findOrRetry(Element $element, $selectorType, $value, $timeout = 10000)
522
    {
523
        if ($timeout <= 0) {
524
            return;
525
        }
526
527
        $item = $element->find($selectorType, $value);
528
529 View Code Duplication
        if ($item) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
530
            return $item;
531
        } else {
532
            $this->getSession()->wait(100);
533
534
            return $this->findOrRetry($element, $selectorType, $value, $timeout - 100);
535
        }
536
    }
537
538
    /**
539
     * Fill Select2 input field and select a value.
540
     *
541
     * @When /^(?:|I )fill in select2 input "(?P<field>(?:[^"]|\\")*)" with "(?P<value>(?:[^"]|\\")*)" and select "(?P<entry>(?:[^"]|\\")*)"$/
542
     */
543
    public function iFillInSelect2InputWithAndSelect($field, $value, $entry)
544
    {
545
        $page = $this->getSession()->getPage();
546
        $this->openField($page, $field);
547
        $this->fillSearchField($page, $field, $value);
548
        $this->selectValue($page, $field, $entry);
549
    }
550
551
    /**
552
     * Open Select2 choice list.
553
     *
554
     * @param DocumentElement $page
555
     * @param string          $field
556
     *
557
     * @throws \Exception
558
     */
559
    private function openField(DocumentElement $page, $field)
560
    {
561
        $fieldName = sprintf('select[name="%s"] + .select2-container', $field);
562
        $inputField = $page->find('css', $fieldName);
563
        if (!$inputField) {
564
            throw new \Exception(sprintf('No field "%s" found', $field));
565
        }
566
        $choice = $inputField->find('css', '.select2-selection');
567
        if (!$choice) {
568
            throw new \Exception(sprintf('No select2 choice found for "%s"', $field));
569
        }
570
        $choice->press();
571
    }
572
573
    /**
574
     * Fill Select2 search field.
575
     *
576
     * @param DocumentElement $page
577
     * @param string          $field
578
     * @param string          $value
579
     *
580
     * @throws \Exception
581
     */
582
    private function fillSearchField(DocumentElement $page, $field, $value)
583
    {
584
        $driver = $this->getSession()->getDriver();
585
        if ('Behat\Mink\Driver\Selenium2Driver' === get_class($driver)) {
586
            // Can't use `$this->getSession()->getPage()->find()` because of https://github.com/minkphp/MinkSelenium2Driver/issues/188
587
            $select2Input = $this->getSession()->getDriver()->getWebDriverSession()->element('xpath', "//html/descendant-or-self::*[@class and contains(concat(' ', normalize-space(@class), ' '), ' select2-search__field ')]");
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Behat\Mink\Driver\DriverInterface as the method getWebDriverSession() does only exist in the following implementations of said interface: Behat\Mink\Driver\Selenium2Driver.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
588
            if (!$select2Input) {
589
                throw new \Exception(sprintf('No field "%s" found', $field));
590
            }
591
            $select2Input->postValue(['value' => [$value]]);
592
        } else {
593
            $select2Input = $page->find('css', '.select2-search__field');
594
            if (!$select2Input) {
595
                throw new \Exception(sprintf('No input found for "%s"', $field));
596
            }
597
            $select2Input->setValue($value);
598
        }
599
        $this->waitForLoadingResults();
600
    }
601
602
    /**
603
     * Select value in choice list.
604
     *
605
     * @param DocumentElement $page
606
     * @param string          $field
607
     * @param string          $value
608
     *
609
     * @throws \Exception
610
     */
611
    private function selectValue(DocumentElement $page, $field, $value)
612
    {
613
        $this->waitForLoadingResults();
614
        $chosenResults = $page->findAll('css', '.select2-results li');
615
        foreach ($chosenResults as $result) {
616
            if ($result->getText() == $value) {
617
                $result->click();
618
619
                return;
620
            }
621
        }
622
        throw new \Exception(sprintf('Value "%s" not found for "%s"', $value, $field));
623
    }
624
625
    /**
626
     * Wait the end of fetching Select2 results.
627
     *
628
     * @param int $time Time to wait in seconds
629
     */
630
    private function waitForLoadingResults($time = 60)
631
    {
632
        for ($i = 0; $i < $time; $i++) {
633
            if (!$this->getSession()->getPage()->find('css', '.select2-results__option.loading-results')) {
634
                return true;
635
            }
636
            sleep(1);
637
        }
638
        throw new \Exception(sprintf('Results are not load after "%d" seconds.', $time));
639
    }
640
}
641