Completed
Push — fix-2494 ( 3153ee )
by Sam
07:19
created

CmsUiContext::iSetTheCmsToMode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Framework\Tests\Behaviour;
4
5
use Behat\Mink\Element\NodeElement;
6
use Behat\Mink\Session;
7
use Behat\Behat\Context\Context;
8
use SilverStripe\BehatExtension\Context\MainContextAwareTrait;
9
use SilverStripe\BehatExtension\Context\RetryableContextTrait;
10
use SilverStripe\BehatExtension\Utility\StepHelper;
11
12
/**
13
 * CmsUiContext
14
 *
15
 * Context used to define steps related to SilverStripe CMS UI like Tree or Panel.
16
 */
17
class CmsUiContext implements Context
18
{
19
    use MainContextAwareTrait;
20
    use StepHelper;
21
22
    /**
23
     * Get Mink session from MinkContext
24
     *
25
     * @return Session
26
     */
27
    public function getSession($name = null)
28
    {
29
        return $this->getMainContext()->getSession($name);
30
    }
31
32
    /**
33
     * Wait until CMS loading overlay isn't present.
34
     * This is an addition to the "ajax steps" logic in
35
     * SilverStripe\BehatExtension\Context\BasicContext
36
     * which also waits for any ajax requests to finish before continuing.
37
     *
38
     * The check also applies in when not in the CMS, which is a structural issue:
39
     * Every step could cause the CMS to be loaded, and we don't know if we're in the
40
     * CMS UI until we run a check.
41
     *
42
     * Excluding scenarios with @modal tag is required,
43
     * because modal dialogs stop any JS interaction
44
     *
45
     * @AfterStep ~@modal
46
     */
47
    public function handleCmsLoadingAfterStep(StepEvent $event)
0 ignored issues
show
Unused Code introduced by
The parameter $event 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...
48
    {
49
        $timeoutMs = $this->getMainContext()->getAjaxTimeout();
50
        $this->getSession()->wait(
51
            $timeoutMs,
52
            "document.getElementsByClassName('cms-content-loading-overlay').length == 0"
53
        );
54
    }
55
56
    /**
57
     * @Then /^I should see the CMS$/
58
     */
59
    public function iShouldSeeTheCms()
60
    {
61
        $page = $this->getSession()->getPage();
62
        $cms_element = $page->find('css', '.cms');
63
        assertNotNull($cms_element, 'CMS not found');
64
    }
65
66
    /**
67
     * @Then /^I should see a "([^"]*)" notice$/
68
     */
69
    public function iShouldSeeANotice($notice)
70
    {
71
        $this->getMainContext()->assertElementContains('.notice-wrap', $notice);
72
    }
73
74
    /**
75
     * @Then /^I should see a "([^"]*)" message$/
76
     */
77
    public function iShouldSeeAMessage($message)
78
    {
79
        $this->getMainContext()->assertElementContains('.message', $message);
80
    }
81
82 View Code Duplication
    protected function getCmsTabsElement()
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...
83
    {
84
        $this->getSession()->wait(
85
            5000,
86
            "window.jQuery && window.jQuery('.cms-content-header-tabs').size() > 0"
87
        );
88
89
        $page = $this->getSession()->getPage();
90
        $cms_content_header_tabs = $page->find('css', '.cms-content-header-tabs');
91
        assertNotNull($cms_content_header_tabs, 'CMS tabs not found');
92
93
        return $cms_content_header_tabs;
94
    }
95
96 View Code Duplication
    protected function getCmsContentToolbarElement()
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...
97
    {
98
        $this->getSession()->wait(
99
            5000,
100
            "window.jQuery && window.jQuery('.cms-content-toolbar').size() > 0 "
101
            . "&& window.jQuery('.cms-content-toolbar').children().size() > 0"
102
        );
103
104
        $page = $this->getSession()->getPage();
105
        $cms_content_toolbar_element = $page->find('css', '.cms-content-toolbar');
106
        assertNotNull($cms_content_toolbar_element, 'CMS content toolbar not found');
107
108
        return $cms_content_toolbar_element;
109
    }
110
111 View Code Duplication
    protected function getCmsTreeElement()
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...
112
    {
113
        $this->getSession()->wait(
114
            5000,
115
            "window.jQuery && window.jQuery('.cms-tree').size() > 0"
116
        );
117
118
        $page = $this->getSession()->getPage();
119
        $cms_tree_element = $page->find('css', '.cms-tree');
120
        assertNotNull($cms_tree_element, 'CMS tree not found');
121
122
        return $cms_tree_element;
123
    }
124
125
    /**
126
     * @Given /^I should see a "([^"]*)" button in CMS Content Toolbar$/
127
     */
128
    public function iShouldSeeAButtonInCmsContentToolbar($text)
129
    {
130
        $cms_content_toolbar_element = $this->getCmsContentToolbarElement();
131
132
        $element = $cms_content_toolbar_element->find('named', array('link_or_button', "'$text'"));
133
        assertNotNull($element, sprintf('%s button not found', $text));
134
    }
135
136
    /**
137
     * @When /^I should see "([^"]*)" in the tree$/
138
     */
139 View Code Duplication
    public function stepIShouldSeeInCmsTree($text)
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...
140
    {
141
        // Wait until visible
142
        $cmsTreeElement = $this->getCmsTreeElement();
143
        $element = $cmsTreeElement->find('named', array('content', "'$text'"));
144
        assertNotNull($element, sprintf('%s not found', $text));
145
    }
146
147
    /**
148
     * @When /^I should not see "([^"]*)" in the tree$/
149
     */
150 View Code Duplication
    public function stepIShouldNotSeeInCmsTree($text)
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...
151
    {
152
        // Wait until not visible
153
        $cmsTreeElement = $this->getCmsTreeElement();
154
        $element = $cmsTreeElement->find('named', array('content', "'$text'"));
155
        assertNull($element, sprintf('%s found', $text));
156
    }
157
158
    /**
159
     * Applies a specific action to an element
160
     *
161
     * @param NodeElement $element Element to act on
162
     * @param string $action Action, which may be one of 'hover', 'double click', 'right click', or 'left click'
163
     * The default 'click' behaves the same as left click
164
     */
165
    protected function interactWithElement($element, $action = 'click')
166
    {
167
        switch ($action) {
168
            case 'hover':
169
                $element->mouseOver();
170
                break;
171
            case 'double click':
172
                $element->doubleClick();
173
                break;
174
            case 'right click':
175
                $element->rightClick();
176
                break;
177
            case 'left click':
178
            case 'click':
179
            default:
180
                $element->click();
181
                break;
182
        }
183
    }
184
185
    /**
186
     * @When /^I (?P<method>(?:(?:double |right |left |)click)|hover) on "(?P<link>[^"]*)" in the context menu/
187
     */
188
    public function stepIClickOnElementInTheContextMenu($method, $link)
189
    {
190
        $context = $this->getMainContext();
191
        // Wait until context menu has appeared
192
        $this->getSession()->wait(
193
            1000,
194
            "window.jQuery && window.jQuery('.jstree-apple-context').size() > 0"
195
        );
196
        $regionObj = $context->getRegionObj('.jstree-apple-context');
197
        assertNotNull($regionObj, "Context menu could not be found");
198
199
        $linkObj = $regionObj->findLink($link);
200
        if (empty($linkObj)) {
201
            throw new \Exception(sprintf(
202
                'The link "%s" was not found in the context menu on the page %s',
203
                $link,
204
                $this->getSession()->getCurrentUrl()
205
            ));
206
        }
207
208
        $this->interactWithElement($linkObj, $method);
209
    }
210
211
    /**
212
     * @When /^I (?P<method>(?:(?:double |right |left |)click)|hover) on "(?P<text>[^"]*)" in the tree$/
213
     */
214
    public function stepIClickOnElementInTheTree($method, $text)
215
    {
216
        $treeEl = $this->getCmsTreeElement();
217
        $treeNode = $treeEl->findLink($text);
218
        assertNotNull($treeNode, sprintf('%s not found', $text));
219
        $this->interactWithElement($treeNode, $method);
220
    }
221
222
    /**
223
     * @When /^I expand the "([^"]*)" CMS Panel$/
224
     */
225
    public function iExpandTheCmsPanel()
226
    {
227
        //Tries to find the first visiable toggle in the page
228
        $page = $this->getSession()->getPage();
229
        $toggle_elements = $page->findAll('css', '.toggle-expand');
230
        assertNotNull($toggle_elements, 'Panel toggle not found');
231
        foreach ($toggle_elements as $toggle) {
232
            if ($toggle->isVisible()) {
233
                $toggle->click();
234
            }
235
        }
236
    }
237
238
    /**
239
     * @When /^I (expand|collapse) the content filters$/
240
     */
241
    public function iExpandTheContentFilters($action)
242
    {
243
        $page = $this->getSession()->getPage();
244
        $filterButton = $page->find('css', '#filters-button');
245
        assertNotNull($filterButton, sprintf('Filter button link not found'));
246
247
        $filterButtonCssClass = $filterButton->getAttribute('class');
248
249
        if ($action === 'expand') {
250
            if (strpos($filterButtonCssClass, 'active') === false) {
251
                $filterButton->click();
252
            }
253
        } else {
254
            if (strpos($filterButtonCssClass, 'active') !== false) {
255
                $filterButton->click();
256
            }
257
        }
258
259
        $this->getSession()->wait(2000, 'window.jQuery(".cms-content-filters:animated").length === 0');
260
261
        // If activating, wait until chosen is activated
262
        if ($action === 'expand') {
263
            $this->getSession()->wait(
264
                2000,
265
                <<<'SCRIPT'
266
(window.jQuery(".cms-content-filters select").length === 0) ||
267
(window.jQuery(".cms-content-filters select:visible.has-chosen").length > 0)
268
SCRIPT
269
            );
270
        }
271
    }
272
273
    /**
274
     * @When /^I (expand|collapse) "([^"]*)" in the tree$/
275
     */
276
    public function iExpandInTheTree($action, $nodeText)
277
    {
278
        //Tries to find the first visiable matched Node in the page
279
        $page = $this->getSession()->getPage();
0 ignored issues
show
Unused Code introduced by
$page is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
280
        $treeEl = $this->getCmsTreeElement();
281
        $treeNode = $treeEl->findLink($nodeText);
282
        assertNotNull($treeNode, sprintf('%s link not found', $nodeText));
283
        $cssIcon = $treeNode->getParent()->getAttribute("class");
284
        if ($action == "expand") {
285
            //ensure it is collapsed
286 View Code Duplication
            if (false === strpos($cssIcon, 'jstree-open')) {
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...
287
                $nodeIcon = $treeNode->getParent()->find('css', '.jstree-icon');
288
                assertTrue($nodeIcon->isVisible(), "CMS node '$nodeText' not found");
289
                $nodeIcon->click();
290
            }
291 View Code Duplication
        } else {
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...
292
            //ensure it is expanded
293
            if (false === strpos($cssIcon, 'jstree-closed')) {
294
                $nodeIcon = $treeNode->getParent()->find('css', '.jstree-icon');
295
                assertTrue($nodeIcon->isVisible(), "CMS node '$nodeText' not found");
296
                $nodeIcon->click();
297
            }
298
        }
299
    }
300
301
    /**
302
     * @When /^I should (not |)see a "([^"]*)" CMS tab$/
303
     */
304
    public function iShouldSeeACmsTab($negate, $tab)
305
    {
306
        $this->getSession()->wait(
307
            5000,
308
            "window.jQuery && window.jQuery('.ui-tabs-nav').size() > 0"
309
        );
310
311
        $page = $this->getSession()->getPage();
312
        $tabsets = $page->findAll('css', '.ui-tabs-nav');
313
        assertNotNull($tabsets, 'CMS tabs not found');
314
315
        $tab_element = null;
316 View Code Duplication
        foreach ($tabsets as $tabset) {
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...
317
            $tab_element = $tabset->find('named', array('link_or_button', "'$tab'"));
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $tab_element is correct as $tabset->find('named', a...r_button', "'{$tab}'")) (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...
318
            if ($tab_element) {
319
                break;
320
            }
321
        }
322
        if ($negate) {
323
            assertNull($tab_element, sprintf('%s tab found', $tab));
324
        } else {
325
            assertNotNull($tab_element, sprintf('%s tab not found', $tab));
326
        }
327
    }
328
329
    /**
330
     * @When /^I click the "([^"]*)" CMS tab$/
331
     */
332
    public function iClickTheCmsTab($tab)
333
    {
334
        $this->getSession()->wait(
335
            5000,
336
            "window.jQuery && window.jQuery('.ui-tabs-nav').size() > 0"
337
        );
338
339
        $page = $this->getSession()->getPage();
340
        $tabsets = $page->findAll('css', '.ui-tabs-nav');
341
        assertNotNull($tabsets, 'CMS tabs not found');
342
343
        $tab_element = null;
344 View Code Duplication
        foreach ($tabsets as $tabset) {
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...
345
            if ($tab_element) {
346
                continue;
347
            }
348
            $tab_element = $tabset->find('named', array('link_or_button', "'$tab'"));
349
        }
350
        assertNotNull($tab_element, sprintf('%s tab not found', $tab));
351
352
        $tab_element->click();
353
    }
354
355
    /**
356
     * @Then /^I can see the preview panel$/
357
     */
358
    public function iCanSeeThePreviewPanel()
359
    {
360
        $this->getMainContext()->assertElementOnPage('.cms-preview');
361
    }
362
363
    /**
364
     * @Given /^the preview contains "([^"]*)"$/
365
     */
366 View Code Duplication
    public function thePreviewContains($content)
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...
367
    {
368
        $driver = $this->getSession()->getDriver();
369
        // TODO Remove once we have native support in Mink and php-webdriver,
370
        // see https://groups.google.com/forum/#!topic/behat/QNhOuGHKEWI
371
        $origWindowName = $driver->getWebDriverSession()->window_handle();
372
373
        $driver->switchToIFrame('cms-preview-iframe');
374
        $this->getMainContext()->assertPageContainsText($content);
375
        $driver->switchToWindow($origWindowName);
376
    }
377
378
    /**
379
     * @Given /^I set the CMS mode to "([^"]*)"$/
380
     */
381
    public function iSetTheCmsToMode($mode)
382
    {
383
        $this->theIFillInTheDropdownWith('Change view mode', $mode);
384
        sleep(1);
385
    }
386
387
    /**
388
     * @Given /^I wait for the preview to load$/
389
     */
390 View Code Duplication
    public function iWaitForThePreviewToLoad()
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...
391
    {
392
        $driver = $this->getSession()->getDriver();
393
        // TODO Remove once we have native support in Mink and php-webdriver,
394
        // see https://groups.google.com/forum/#!topic/behat/QNhOuGHKEWI
395
        $origWindowName = $driver->getWebDriverSession()->window_handle();
396
397
        $driver->switchToIFrame('cms-preview-iframe');
398
        $this->getSession()->wait(
399
            5000,
400
            "window.jQuery && !window.jQuery('iframe[name=cms-preview-iframe]').hasClass('loading')"
401
        );
402
        $driver->switchToWindow($origWindowName);
403
    }
404
405
    /**
406
     * @Given /^I switch the preview to "([^"]*)"$/
407
     */
408
    public function iSwitchThePreviewToMode($mode)
409
    {
410
        $controls = $this->getSession()->getPage()->find('css', '.cms-preview-controls');
411
        assertNotNull($controls, 'Preview controls not found');
412
413
        $label = $controls->find('xpath', sprintf(
414
            './/*[count(*)=0 and contains(text(), \'%s\')]',
415
            $mode
416
        ));
417
        assertNotNull($label, 'Preview mode switch not found');
418
419
        $label->click();
420
421
        $this->iWaitForThePreviewToLoad();
422
    }
423
424
    /**
425
     * @Given /^the preview does not contain "([^"]*)"$/
426
     */
427 View Code Duplication
    public function thePreviewDoesNotContain($content)
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...
428
    {
429
        $driver = $this->getSession()->getDriver();
430
        // TODO Remove once we have native support in Mink and php-webdriver,
431
        // see https://groups.google.com/forum/#!topic/behat/QNhOuGHKEWI
432
        $origWindowName = $driver->getWebDriverSession()->window_handle();
433
434
        $driver->switchToIFrame('cms-preview-iframe');
435
        $this->getMainContext()->assertPageNotContainsText($content);
436
        $driver->switchToWindow($origWindowName);
437
    }
438
439
    /**
440
     * When I follow "my link" in preview
441
     *
442
     * @When /^(?:|I )follow "(?P<link>(?:[^"]|\\")*)" in preview$/
443
     */
444 View Code Duplication
    public function clickLinkInPreview($link)
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...
445
    {
446
        $driver = $this->getSession()->getDriver();
447
        // TODO Remove once we have native support in Mink and php-webdriver,
448
        // see https://groups.google.com/forum/#!topic/behat/QNhOuGHKEWI
449
        $origWindowName = $driver->getWebDriverSession()->window_handle();
450
        $driver->switchToIFrame('cms-preview-iframe');
451
452
        $link = $this->fixStepArgument($link);
453
        $this->getSession()->getPage()->clickLink($link);
454
455
        $driver->switchToWindow($origWindowName);
456
    }
457
458
    /**
459
     * When I press "submit" in preview
460
     *
461
     * @When /^(?:|I )press "(?P<button>(?:[^"]|\\")*)" in preview$/
462
     */
463 View Code Duplication
    public function pressButtonInPreview($button)
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...
464
    {
465
        $driver = $this->getSession()->getDriver();
466
        // TODO Remove once we have native support in Mink and php-webdriver,
467
        // see https://groups.google.com/forum/#!topic/behat/QNhOuGHKEWI
468
        $origWindowName = $driver->getWebDriverSession()->window_handle();
469
        $driver->switchToIFrame('cms-preview-iframe');
470
471
        $button = $this->fixStepArgument($button);
472
        $this->getSession()->getPage()->pressButton($button);
473
474
        $driver->switchToWindow($origWindowName);
475
    }
476
477
    /**
478
     * Workaround for chosen.js dropdowns or tree dropdowns which hide the original dropdown field.
479
     *
480
     * @When /^(?:|I )fill in the "(?P<field>(?:[^"]|\\")*)" dropdown with "(?P<value>(?:[^"]|\\")*)"$/
481
     * @When /^(?:|I )fill in "(?P<value>(?:[^"]|\\")*)" for the "(?P<field>(?:[^"]|\\")*)" dropdown$/
482
     */
483
    public function theIFillInTheDropdownWith($field, $value)
484
    {
485
        $field = $this->fixStepArgument($field);
486
        $value = $this->fixStepArgument($value);
487
488
        $nativeField = $this->getSession()->getPage()->find(
489
            'named',
490
            array('select', $this->getSession()->getSelectorsHandler()->xpathLiteral($field))
0 ignored issues
show
Deprecated Code introduced by
The method Behat\Mink\Selector\Sele...Handler::xpathLiteral() has been deprecated with message: since Mink 1.7. Use \Behat\Mink\Selector\Xpath\Escaper::escapeLiteral when building Xpath or pass the unescaped value when using the named selector.

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...
491
        );
492
        if ($nativeField && $nativeField->isVisible()) {
493
            $nativeField->selectOption($value);
494
            return;
495
        }
496
497
        // Given the fuzzy matching, we might get more than one matching field.
498
        $formFields = array();
499
500
        // Find by label
501
        $formField = $this->getSession()->getPage()->findField($field);
502
        if ($formField && $formField->getTagName() == 'select') {
503
            $formFields[] = $formField;
504
        }
505
506
        // Fall back to finding by title (for dropdowns without a label)
507
        if (!$formFields) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $formFields of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
508
            $formFields = $this->getSession()->getPage()->findAll(
509
                'xpath',
510
                sprintf(
511
                    '//*[self::select][(./@title="%s")]',
512
                    $field
513
                )
514
            );
515
        }
516
517
        // Find by name (incl. hidden fields)
518
        if (!$formFields) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $formFields of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
519
            $formFields = $this->getSession()->getPage()->findAll('xpath', "//*[@name='$field']");
520
        }
521
522
        // Find by label
523
        if (!$formFields) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $formFields of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
524
            $label = $this->getSession()->getPage()->find('xpath', "//label[.='$field']");
525
            if ($label && $for = $label->getAttribute('for')) {
526
                $formField = $this->getSession()->getPage()->find('xpath', "//*[@id='$for']");
527
                if ($formField) {
528
                    $formFields[] = $formField;
529
                }
530
            }
531
        }
532
533
        assertGreaterThan(0, count($formFields), sprintf(
534
            'Chosen.js dropdown named "%s" not found',
535
            $field
536
        ));
537
538
        // Traverse up to field holder
539
        $container = null;
540
        foreach ($formFields as $formField) {
541
            $container = $this->findParentByClass($formField, 'field');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $container is correct as $this->findParentByClass($formField, 'field') (which targets SilverStripe\Framework\T...xt::findParentByClass()) 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...
542
            if ($container) {
543
                break; // Default to first visible container
544
            }
545
        }
546
547
        assertNotNull($container, 'Chosen.js field container not found');
548
549
        // Click on newly expanded list element, indirectly setting the dropdown value
550
        $linkEl = $container->find('xpath', './/a');
0 ignored issues
show
Bug introduced by
The method find cannot be called on $container (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
551
        assertNotNull($linkEl, 'Chosen.js link element not found');
552
        $this->getSession()->wait(100); // wait for dropdown overlay to appear
553
        $linkEl->click();
554
555
        if (in_array('treedropdown', explode(' ', $container->getAttribute('class')))) {
0 ignored issues
show
Bug introduced by
The method getAttribute cannot be called on $container (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
556
            // wait for ajax dropdown to load
557
            $this->getSession()->wait(
558
                5000,
559
                "window.jQuery && "
560
                . "window.jQuery('#" . $container->getAttribute('id') . " .treedropdownfield-panel li').length > 0"
0 ignored issues
show
Bug introduced by
The method getAttribute cannot be called on $container (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
561
            );
562
        } else {
563
            // wait for dropdown overlay to appear (might be animated)
564
            $this->getSession()->wait(300);
565
        }
566
567
        $listEl = $container->find('xpath', sprintf('.//li[contains(normalize-space(string(.)), \'%s\')]', $value));
0 ignored issues
show
Bug introduced by
The method find cannot be called on $container (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
568
        if (null === $listEl) {
569
            throw new \InvalidArgumentException(sprintf(
570
                'Chosen.js list element with title "%s" not found',
571
                $value
572
            ));
573
        }
574
575
        $listLinkEl = $listEl->find('xpath', './/a');
576
        if ($listLinkEl) {
577
            $listLinkEl->click();
578
        } else {
579
            $listEl->click();
580
        }
581
    }
582
583
    /**
584
     * Returns fixed step argument (with \\" replaced back to ").
585
     *
586
     * @param string $argument
587
     *
588
     * @return string
589
     */
590
    protected function fixStepArgument($argument)
591
    {
592
        return str_replace('\\"', '"', $argument);
593
    }
594
595
    /**
596
     * Returns the closest parent element having a specific class attribute.
597
     *
598
     * @param  NodeElement $el
599
     * @param  String  $class
600
     * @return Element|null
601
     */
602
    protected function findParentByClass(NodeElement $el, $class)
603
    {
604
        $container = $el->getParent();
605
        while ($container && $container->getTagName() != 'body') {
606
            if ($container->isVisible() && in_array($class, explode(' ', $container->getAttribute('class')))) {
607
                return $container;
608
            }
609
            $container = $container->getParent();
610
        }
611
612
        return null;
613
    }
614
}
615