Passed
Push — nondestructive-enum ( eef0c9...88175c )
by Sam
07:14
created

CmsFormsContext::findInputByLabelContent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 1
dl 0
loc 18
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Framework\Tests\Behaviour;
4
5
use BadMethodCallException;
6
use Behat\Behat\Context\Context;
0 ignored issues
show
Bug introduced by
The type Behat\Behat\Context\Context was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7
use Behat\Mink\Exception\ElementHtmlException;
0 ignored issues
show
Bug introduced by
The type Behat\Mink\Exception\ElementHtmlException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Behat\Gherkin\Node\TableNode;
0 ignored issues
show
Bug introduced by
The type Behat\Gherkin\Node\TableNode was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use Behat\Mink\Session;
0 ignored issues
show
Bug introduced by
The type Behat\Mink\Session was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use SilverStripe\BehatExtension\Context\MainContextAwareTrait;
0 ignored issues
show
Bug introduced by
The type SilverStripe\BehatExtens...t\MainContextAwareTrait was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use SilverStripe\BehatExtension\Utility\StepHelper;
0 ignored issues
show
Bug introduced by
The type SilverStripe\BehatExtension\Utility\StepHelper was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use Symfony\Component\DomCrawler\Crawler;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\DomCrawler\Crawler was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use Behat\Mink\Element\NodeElement;
0 ignored issues
show
Bug introduced by
The type Behat\Mink\Element\NodeElement was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use SilverStripe\SiteConfig\SiteConfig;
0 ignored issues
show
Bug introduced by
The type SilverStripe\SiteConfig\SiteConfig was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
16
/**
17
 * CmsFormsContext
18
 *
19
 * Context used to define steps related to forms inside CMS.
20
 */
21
class CmsFormsContext implements Context
22
{
23
    use MainContextAwareTrait;
24
    use StepHelper;
25
26
    /**
27
     * Get Mink session from MinkContext
28
     *
29
     * @param string $name
30
     * @return Session
31
     */
32
    public function getSession($name = null)
33
    {
34
        return $this->getMainContext()->getSession($name);
35
    }
36
37
    /**
38
     * Returns fixed step argument (with \\" replaced back to ").
39
     * Copied from {@see MinkContext}
40
     *
41
     * @param string $argument
42
     * @return string
43
     */
44
    protected function fixStepArgument($argument)
45
    {
46
        return str_replace('\\"', '"', $argument);
47
    }
48
49
    /**
50
     * @Then /^I should( not? |\s*)see an edit page form$/
51
     */
52
    public function stepIShouldSeeAnEditPageForm($negative)
53
    {
54
        $page = $this->getSession()->getPage();
55
56
        $form = $page->find('css', '#Form_EditForm');
57
        if (trim($negative)) {
58
            assertNull($form, 'I should not see an edit page form');
59
        } else {
60
            assertNotNull($form, 'I should see an edit page form');
61
        }
62
    }
63
64
    /**
65
     * @When /^I fill in the "(?P<field>(?:[^"]|\\")*)" HTML field with "(?P<value>(?:[^"]|\\")*)"$/
66
     * @When /^I fill in "(?P<value>(?:[^"]|\\")*)" for the "(?P<field>(?:[^"]|\\")*)" HTML field$/
67
     */
68
    public function stepIFillInTheHtmlFieldWith($field, $value)
69
    {
70
        $inputField = $this->getHtmlField($field);
71
        $value = $this->fixStepArgument($value);
72
73
        $this->getSession()->evaluateScript(sprintf(
74
            "jQuery('#%s').entwine('ss').getEditor().setContent('%s')",
75
            $inputField->getAttribute('id'),
76
            addcslashes($value, "'")
77
        ));
78
        $this->getSession()->evaluateScript(sprintf(
79
            "jQuery('#%s').entwine('ss').getEditor().save()",
80
            $inputField->getAttribute('id')
81
        ));
82
    }
83
84
    /**
85
     * @When /^I append "(?P<value>(?:[^"]|\\")*)" to the "(?P<field>(?:[^"]|\\")*)" HTML field$/
86
     */
87
    public function stepIAppendTotheHtmlField($field, $value)
88
    {
89
        $inputField = $this->getHtmlField($field);
90
        $value = $this->fixStepArgument($value);
91
92
        $this->getSession()->evaluateScript(sprintf(
93
            "jQuery('#%s').entwine('ss').getEditor().insertContent('%s')",
94
            $inputField->getAttribute('id'),
95
            addcslashes($value, "'")
96
        ));
97
    }
98
99
    /**
100
     * @Then /^the "(?P<locator>(?:[^"]|\\")*)" HTML field should(?P<negative> not? |\s*)contain "(?P<html>.*)"$/
101
     */
102
    public function theHtmlFieldShouldContain($locator, $negative, $html)
103
    {
104
        $element = $this->getHtmlField($locator);
105
        $actual = $element->getValue();
106
        $regex = '/' . preg_quote($html, '/') . '/ui';
107
        $failed = false;
108
109
        if (trim($negative)) {
110
            if (preg_match($regex, $actual)) {
111
                $failed = true;
112
            }
113
        } else {
114
            if (!preg_match($regex, $actual)) {
115
                $failed = true;
116
            }
117
        }
118
119
        if ($failed) {
120
            $message = sprintf(
121
                'The string "%s" should%sbe found in the HTML of the element matching name "%s". Actual content: "%s"',
122
                $html,
123
                $negative,
124
                $locator,
125
                $actual
126
            );
127
            throw new ElementHtmlException($message, $this->getSession(), $element);
128
        }
129
    }
130
131
	// @codingStandardsIgnoreStart
132
	/**
133
	 * Checks formatting in the HTML field, by analyzing the HTML node surrounding
134
	 * the text for certain properties.
135
	 *
136
	 * Example: Given "my text" in the "Content" HTML field should be right aligned
137
	 * Example: Given "my text" in the "Content" HTML field should not be bold
138
	 *
139
	 * @todo Use an actual DOM parser for more accurate assertions
140
	 *
141
	 * @Given /^"(?P<text>([^"]*))" in the "(?P<field>(?:[^"]|\\")*)" HTML field should(?P<negate>(?: not)?) be (?P<formatting>(.*))$/
142
	 */
143
	public function stepContentInHtmlFieldShouldHaveFormatting($text, $field, $negate, $formatting) {
144
		$inputField = $this->getHtmlField($field);
145
146
		$crawler = new Crawler($inputField->getValue());
147
		$matchedNode = null;
148
		foreach($crawler->filterXPath('//*') as $node) {
149
			if(
150
				$node->firstChild
151
				&& $node->firstChild->nodeType == XML_TEXT_NODE
152
				&& stripos($node->firstChild->nodeValue, $text) !== FALSE
153
			) {
154
				$matchedNode = $node;
155
			}
156
		}
157
		assertNotNull($matchedNode);
158
159
		$assertFn = $negate ? 'assertNotEquals' : 'assertEquals';
160
		if($formatting == 'bold') {
161
			call_user_func($assertFn, 'strong', $matchedNode->nodeName);
162
		} else if($formatting == 'left aligned') {
163
			if($matchedNode->getAttribute('class')) {
164
				call_user_func($assertFn, 'text-left', $matchedNode->getAttribute('class'));
165
			}
166
		} else if($formatting == 'right aligned') {
167
			call_user_func($assertFn, 'text-right', $matchedNode->getAttribute('class'));
168
		}
169
	}
170
	// @codingStandardsIgnoreEnd
171
172
    /**
173
     * Selects the first textual match in the HTML editor. Does not support
174
     * selection across DOM node boundaries.
175
     *
176
     * @When /^I select "(?P<text>([^"]*))" in the "(?P<field>(?:[^"]|\\")*)" HTML field$/
177
     */
178
    public function stepIHighlightTextInHtmlField($text, $field)
179
    {
180
        $inputField = $this->getHtmlField($field);
181
        $inputFieldId = $inputField->getAttribute('id');
182
        $text = addcslashes($text, "'");
183
184
        $js = <<<JS
185
// TODO <IE9 support
186
// TODO Allow text matches across nodes
187
var editor = jQuery('#$inputFieldId').entwine('ss').getEditor(),
188
	doc = editor.getInstance().getDoc(),
189
	sel = editor.getInstance().selection,
190
	rng = document.createRange(),
191
	matched = false;
192
193
editor.getInstance().focus();
194
jQuery(doc).find('body *').each(function() {
195
	if(!matched) {
196
		for(var i=0;i<this.childNodes.length;i++) {
197
			if(!matched && this.childNodes[i].nodeValue && this.childNodes[i].nodeValue.match('$text')) {
198
				rng.setStart(this.childNodes[i], this.childNodes[i].nodeValue.indexOf('$text'));
199
				rng.setEnd(this.childNodes[i], this.childNodes[i].nodeValue.indexOf('$text') + '$text'.length);
200
				sel.setRng(rng);
201
				editor.getInstance().nodeChanged();
202
				matched = true;
203
				break;
204
			}
205
		}
206
	}
207
});
208
JS;
209
210
        $this->getSession()->executeScript($js);
211
    }
212
213
    /**
214
     * @Given /^I should( not? |\s*)see a "([^"]*)" field$/
215
     */
216
    public function iShouldSeeAField($negative, $text)
217
    {
218
        $page = $this->getSession()->getPage();
219
        $els = $page->findAll('named', array('field', "'$text'"));
220
        $matchedEl = null;
221
        /** @var NodeElement $el */
222
        foreach ($els as $el) {
223
            if ($el->isVisible()) {
224
                $matchedEl = $el;
225
            }
226
        }
227
228
        if (trim($negative)) {
229
            assertNull($matchedEl);
230
        } else {
231
            assertNotNull($matchedEl);
232
        }
233
    }
234
235
    /**
236
     * Click on the element with the provided CSS Selector
237
     *
238
     * @When /^I press the "([^"]*)" HTML field button$/
239
     */
240
    public function iClickOnTheHtmlFieldButton($button)
241
    {
242
        $xpath = "//*[@aria-label='" . $button . "']";
243
        $session = $this->getSession();
244
        $element = $session->getPage()->find('xpath', $xpath);
245
        if (null === $element) {
246
            // If it can't find the exact name, find one that starts with the phrase
247
            // Helpful for "Insert link" which has a conditional label for keyboard shortcut
248
            $xpath = "//*[starts-with(@aria-label, '" . $button . "')]";
249
            $element = $session->getPage()->find('xpath', $xpath);
250
251
            if (null === $element) {
252
                throw new \InvalidArgumentException(sprintf('Could not find element with xpath %s', $xpath));
253
            };
254
        }
255
256
        $element->click();
257
    }
258
259
    /*
260
     * @example Given the CMS settings has the following data
261
     *  | Title | My site title |
262
     *  | Theme | My site theme |
263
     * @Given /^the CMS settings have the following data$/
264
     */
265
    public function theCmsSettingsHasData(TableNode $fieldsTable)
266
    {
267
        $fields = $fieldsTable->getRowsHash();
268
        $siteConfig = SiteConfig::get()->first();
269
        foreach ($fields as $field => $value) {
270
            $siteConfig->$field = $value;
271
        }
272
        $siteConfig->write();
273
        $siteConfig->flushCache();
274
    }
275
276
    /**
277
     * Select a value in the tree dropdown field
278
     *
279
     * NOTE: This is react specific, may need to move to its own react section later
280
     *
281
     * @When /^I select "([^"]*)" in the "([^"]*)" tree dropdown$/
282
     */
283
    public function iSelectValueInTreeDropdown($text, $selector)
284
    {
285
        $page = $this->getSession()->getPage();
286
        /** @var NodeElement $parentElement */
287
        $parentElement = null;
288
        $this->retryThrowable(function () use (&$parentElement, &$page, $selector) {
289
            $parentElement = $page->find('css', $selector);
290
            assertNotNull($parentElement, sprintf('"%s" element not found', $selector));
291
            $page = $this->getSession()->getPage();
292
        });
293
294
        $this->retryThrowable(function () use ($parentElement, $selector) {
295
            $dropdown = $parentElement->find('css', '.Select-arrow');
296
            assertNotNull($dropdown, sprintf('Unable to find the dropdown in "%s"', $selector));
297
            $dropdown->click();
298
        });
299
300
        $this->retryThrowable(function () use ($text, $parentElement, $selector) {
301
            $element = $parentElement->find('xpath', sprintf('//*[count(*)=0 and contains(.,"%s")]', $text));
302
            assertNotNull($element, sprintf('"%s" not found in "%s"', $text, $selector));
303
            $element->click();
304
        });
305
    }
306
307
    /**
308
     * Locate an HTML editor field
309
     *
310
     * @param string $locator Raw html field identifier as passed from
311
     * @return NodeElement
312
     */
313
    protected function getHtmlField($locator)
314
    {
315
        $locator = $this->fixStepArgument($locator);
316
        $page = $this->getSession()->getPage();
317
        
318
        // Searching by name is usually good...
319
        $element = $page->find('css', 'textarea.htmleditor[name=\'' . $locator . '\']');
320
        
321
        if ($element === null) {
322
            $element = $this->findInputByLabelContent($locator);
323
        }
324
        
325
        assertNotNull($element, sprintf('HTML field "%s" not found', $locator));
326
        return $element;
327
    }
328
329
    protected function findInputByLabelContent($locator)
330
    {
331
        $page = $this->getSession()->getPage();
332
        $label = $page->findAll('xpath', sprintf('//label[contains(text(), \'%s\')]', $locator));
333
334
        if (empty($label)) {
335
            return null;
336
        }
337
338
        assertCount(1, $label, sprintf(
339
            'Found more than one element containing the phrase "%s".',
340
            $locator
341
        ));
342
343
        $label = array_shift($label);
344
345
        $fieldId = $label->getAttribute('for');
346
        return $page->find('css', '#' . $fieldId);
347
    }
348
349
    /**
350
     * @Given /^the "([^"]*)" field ((?:does not have)|(?:has)) property "([^"]*)"$/
351
     */
352
    public function assertTheFieldHasProperty($name, $cond, $property)
353
    {
354
        $name = $this->fixStepArgument($name);
355
        $property = $this->fixStepArgument($property);
356
357
        $context = $this->getMainContext();
358
        $fieldObj = $context->assertSession()->fieldExists($name);
359
360
        // Check property
361
        $hasProperty = $fieldObj->hasAttribute($property);
362
        switch ($cond) {
363
            case 'has':
364
                assert($hasProperty, "Field $name does not have property $property");
365
                break;
366
            case 'does not have':
367
                assert(!$hasProperty, "Field $name should not have property $property");
368
                break;
369
            default:
370
                throw new BadMethodCallException("Invalid condition");
371
        }
372
    }
373
374
    /**
375
     * @When /^I switch to the "([^"]*)" iframe$/
376
     * @param string $id iframe id property
377
     */
378
    public function stepSwitchToTheFrame($id)
379
    {
380
        $this->getMainContext()->getSession()->getDriver()->switchToIFrame($id);
381
    }
382
383
    /**
384
     * @When /^I am not in an iframe$/
385
     */
386
    public function stepSwitchToParentFrame()
387
    {
388
        $this->getMainContext()->getSession()->getDriver()->switchToIFrame(null);
389
    }
390
391
    /**
392
     * @When /^my session expires$/
393
     */
394
    public function stepMySessionExpires()
395
    {
396
        // Destroy cookie to detach session
397
        $this->getMainContext()->getSession()->setCookie('PHPSESSID', null);
398
    }
399
400
    /**
401
     * @When /^I should see the "([^"]*)" button in the "([^"]*)" gridfield for the "([^"]*)" row$/
402
     * @param string $buttonLabel
403
     * @param string $gridFieldName
404
     * @param string $rowName
405
     */
406
    public function assertIShouldSeeTheGridFieldButtonForRow($buttonLabel, $gridFieldName, $rowName)
407
    {
408
        $button = $this->getGridFieldButton($gridFieldName, $rowName, $buttonLabel);
409
        assertNotNull($button, sprintf('Button "%s" not found', $buttonLabel));
410
    }
411
412
    /**
413
     * @When /^I should not see the "([^"]*)" button in the "([^"]*)" gridfield for the "([^"]*)" row$/
414
     * @param string $buttonLabel
415
     * @param string $gridFieldName
416
     * @param string $rowName
417
     */
418
    public function assertIShouldNotSeeTheGridFieldButtonForRow($buttonLabel, $gridFieldName, $rowName)
419
    {
420
        $button = $this->getGridFieldButton($gridFieldName, $rowName, $buttonLabel);
421
        assertNull($button, sprintf('Button "%s" found', $buttonLabel));
422
    }
423
424
    /**
425
     * @When /^I click the "([^"]*)" button in the "([^"]*)" gridfield for the "([^"]*)" row$/
426
     * @param string $buttonLabel
427
     * @param string $gridFieldName
428
     * @param string $rowName
429
     */
430
    public function stepIClickTheGridFieldButtonForRow($buttonLabel, $gridFieldName, $rowName)
431
    {
432
        $button = $this->getGridFieldButton($gridFieldName, $rowName, $buttonLabel);
433
        assertNotNull($button, sprintf('Button "%s" not found', $buttonLabel));
434
435
        $button->click();
436
    }
437
438
    /**
439
     * Finds a button in the gridfield row
440
     *
441
     * @param $gridFieldName
442
     * @param $rowName
443
     * @param $buttonLabel
444
     * @return $button
0 ignored issues
show
Documentation Bug introduced by
The doc comment $button at position 0 could not be parsed: Unknown type name '$button' at position 0 in $button.
Loading history...
445
     */
446
    protected function getGridFieldButton($gridFieldName, $rowName, $buttonLabel)
447
    {
448
        $page = $this->getSession()->getPage();
449
        $gridField = $page->find('xpath', sprintf('//*[@data-name="%s"]', $gridFieldName));
450
        assertNotNull($gridField, sprintf('Gridfield "%s" not found', $gridFieldName));
451
452
        $name = $gridField->find('xpath', sprintf('//*[count(*)=0 and contains(.,"%s")]', $rowName));
453
        if (!$name) {
454
            return null;
455
        }
456
457
        if ($dropdownButton = $name->getParent()->find('css', '.action-menu__toggle')) {
458
            $dropdownButton->click();
459
        }
460
461
        $button = $name->getParent()->find('named', array('link_or_button', $buttonLabel));
462
463
        return $button;
464
    }
465
466
    /**
467
     * @When /^I click the "([^"]*)" option in the "([^"]*)" listbox$/
468
     * @param $optionLabel
469
     * @param $fieldName
470
     */
471
    public function stepIClickTheListBoxOption($optionLabel, $fieldName)
472
    {
473
        $page = $this->getSession()->getPage();
474
        $listBox = $page->find('xpath', sprintf('//*[@name="%s[]"]', $fieldName));
475
        assertNotNull($listBox, sprintf('The listbox %s is not found', $fieldName));
476
477
        $option = $listBox->getParent()
478
            ->find('css', '.chosen-choices')
479
            ->find('xpath', sprintf('//*[count(*)=0 and contains(.,"%s")]', $optionLabel));
480
        assertNotNull($option, sprintf('Option %s is not found', $optionLabel));
481
482
        $button = $option->getParent()->find('css', 'a');
483
484
        $button->click();
485
    }
486
}
487