Test Setup Failed
Push — master ( 210134...c17796 )
by Damian
03:18
created

tests/behat/src/CmsFormsContext.php (4 issues)

1
<?php
2
3
namespace SilverStripe\Framework\Tests\Behaviour;
4
5
use BadMethodCallException;
6
use Behat\Behat\Context\Context;
7
use Behat\Mink\Exception\ElementHtmlException;
8
use Behat\Gherkin\Node\TableNode;
9
use SilverStripe\BehatExtension\Context\MainContextAwareTrait;
10
use SilverStripe\BehatExtension\Utility\StepHelper;
11
use Symfony\Component\DomCrawler\Crawler;
12
use Behat\Mink\Element\NodeElement;
13
use SilverStripe\SiteConfig\SiteConfig;
14
15
/**
16
 * CmsFormsContext
17
 *
18
 * Context used to define steps related to forms inside CMS.
19
 */
20
class CmsFormsContext implements Context
21
{
22
    use MainContextAwareTrait;
23
    use StepHelper;
24
25
    /**
26
     * Get Mink session from MinkContext
27
     */
28
    public function getSession($name = null)
29
    {
30
        return $this->getMainContext()->getSession($name);
31
    }
32
33
    /**
34
     * Returns fixed step argument (with \\" replaced back to ").
35
     * Copied from {@see MinkContext}
36
     *
37
     * @param string $argument
38
     * @return string
39
     */
40
    protected function fixStepArgument($argument)
41
    {
42
        return str_replace('\\"', '"', $argument);
43
    }
44
45
    /**
46
     * @Then /^I should( not? |\s*)see an edit page form$/
47
     */
48
    public function stepIShouldSeeAnEditPageForm($negative)
49
    {
50
        $page = $this->getSession()->getPage();
51
52
        $form = $page->find('css', '#Form_EditForm');
53
        if (trim($negative)) {
54
            assertNull($form, 'I should not see an edit page form');
55
        } else {
56
            assertNotNull($form, 'I should see an edit page form');
57
        }
58
    }
59
60
    /**
61
     * @When /^I fill in the "(?P<field>(?:[^"]|\\")*)" HTML field with "(?P<value>(?:[^"]|\\")*)"$/
62
     * @When /^I fill in "(?P<value>(?:[^"]|\\")*)" for the "(?P<field>(?:[^"]|\\")*)" HTML field$/
63
     */
64
    public function stepIFillInTheHtmlFieldWith($field, $value)
65
    {
66
        $inputField = $this->getHtmlField($field);
67
        $value = $this->fixStepArgument($value);
68
69
        $this->getSession()->evaluateScript(sprintf(
70
            "jQuery('#%s').entwine('ss').getEditor().setContent('%s')",
71
            $inputField->getAttribute('id'),
72
            addcslashes($value, "'")
73
        ));
74
    }
75
76
    /**
77
     * @When /^I append "(?P<value>(?:[^"]|\\")*)" to the "(?P<field>(?:[^"]|\\")*)" HTML field$/
78
     */
79
    public function stepIAppendTotheHtmlField($field, $value)
80
    {
81
        $inputField = $this->getHtmlField($field);
82
        $value = $this->fixStepArgument($value);
83
84
        $this->getSession()->evaluateScript(sprintf(
85
            "jQuery('#%s').entwine('ss').getEditor().insertContent('%s')",
86
            $inputField->getAttribute('id'),
87
            addcslashes($value, "'")
88
        ));
89
    }
90
91
    /**
92
     * @Then /^the "(?P<locator>(?:[^"]|\\")*)" HTML field should(?P<negative> not? |\s*)contain "(?P<html>.*)"$/
93
     */
94
    public function theHtmlFieldShouldContain($locator, $negative, $html)
95
    {
96
        $element = $this->getHtmlField($locator);
97
        $actual = $element->getValue();
98
        $regex = '/' . preg_quote($html, '/') . '/ui';
99
        $failed = false;
100
101
        if (trim($negative)) {
102
            if (preg_match($regex, $actual)) {
103
                $failed = true;
104
            }
105
        } else {
106
            if (!preg_match($regex, $actual)) {
107
                $failed = true;
108
            }
109
        }
110
111
        if ($failed) {
112
            $message = sprintf(
113
                'The string "%s" should%sbe found in the HTML of the element matching name "%s". Actual content: "%s"',
114
                $html,
115
                $negative,
116
                $locator,
117
                $actual
118
            );
119
            throw new ElementHtmlException($message, $this->getSession(), $element);
120
        }
121
    }
122
123
	// @codingStandardsIgnoreStart
124
	/**
125
	 * Checks formatting in the HTML field, by analyzing the HTML node surrounding
126
	 * the text for certain properties.
127
	 *
128
	 * Example: Given "my text" in the "Content" HTML field should be right aligned
129
	 * Example: Given "my text" in the "Content" HTML field should not be bold
130
	 *
131
	 * @todo Use an actual DOM parser for more accurate assertions
132
	 *
133
	 * @Given /^"(?P<text>([^"]*))" in the "(?P<field>(?:[^"]|\\")*)" HTML field should(?P<negate>(?: not)?) be (?P<formatting>(.*))$/
134
	 */
135
	public function stepContentInHtmlFieldShouldHaveFormatting($text, $field, $negate, $formatting) {
136
		$inputField = $this->getHtmlField($field);
137
138
		$crawler = new Crawler($inputField->getValue());
139
		$matchedNode = null;
140
		foreach($crawler->filterXPath('//*') as $node) {
141
			if(
142
				$node->firstChild
143
				&& $node->firstChild->nodeType == XML_TEXT_NODE
144
				&& stripos($node->firstChild->nodeValue, $text) !== FALSE
145
			) {
146
				$matchedNode = $node;
147
			}
148
		}
149
		assertNotNull($matchedNode);
150
151
		$assertFn = $negate ? 'assertNotEquals' : 'assertEquals';
152
		if($formatting == 'bold') {
153
			call_user_func($assertFn, 'strong', $matchedNode->nodeName);
154
		} else if($formatting == 'left aligned') {
155
			if($matchedNode->getAttribute('style')) {
156
				call_user_func($assertFn, 'text-align: left;', $matchedNode->getAttribute('style'));
157
			}
158
		} else if($formatting == 'right aligned') {
159
			call_user_func($assertFn, 'text-align: right;', $matchedNode->getAttribute('style'));
160
		}
161
	}
162
	// @codingStandardsIgnoreEnd
163
164
    /**
165
     * Selects the first textual match in the HTML editor. Does not support
166
     * selection across DOM node boundaries.
167
     *
168
     * @When /^I select "(?P<text>([^"]*))" in the "(?P<field>(?:[^"]|\\")*)" HTML field$/
169
     */
170
    public function stepIHighlightTextInHtmlField($text, $field)
171
    {
172
        $inputField = $this->getHtmlField($field);
173
        $inputFieldId = $inputField->getAttribute('id');
174
        $text = addcslashes($text, "'");
175
176
        $js = <<<JS
177
// TODO <IE9 support
178
// TODO Allow text matches across nodes
179
var editor = jQuery('#$inputFieldId').entwine('ss').getEditor(),
180
	doc = editor.getInstance().getDoc(),
181
	sel = editor.getInstance().selection,
182
	rng = document.createRange(),
183
	matched = false;
184
185
editor.getInstance().focus();
186
jQuery(doc).find('body *').each(function() {
187
	if(!matched) {
188
		for(var i=0;i<this.childNodes.length;i++) {
189
			if(!matched && this.childNodes[i].nodeValue && this.childNodes[i].nodeValue.match('$text')) {
190
				rng.setStart(this.childNodes[i], this.childNodes[i].nodeValue.indexOf('$text'));
191
				rng.setEnd(this.childNodes[i], this.childNodes[i].nodeValue.indexOf('$text') + '$text'.length);
192
				sel.setRng(rng);
193
				editor.getInstance().nodeChanged();
194
				matched = true;
195
				break;
196
			}
197
		}
198
	}
199
});
200
JS;
201
202
        $this->getSession()->executeScript($js);
203
    }
204
205
    /**
206
     * @Given /^I should( not? |\s*)see a "([^"]*)" field$/
207
     */
208
    public function iShouldSeeAField($negative, $text)
209
    {
210
        $page = $this->getSession()->getPage();
211
        $els = $page->findAll('named', array('field', "'$text'"));
212
        $matchedEl = null;
213
        foreach ($els as $el) {
214
            if ($el->isVisible()) {
215
                $matchedEl = $el;
216
            }
217
        }
218
219
        if (trim($negative)) {
220
            assertNull($matchedEl);
221
        } else {
222
            assertNotNull($matchedEl);
223
        }
224
    }
225
226
    /**
227
     * Click on the element with the provided CSS Selector
228
     *
229
     * @When /^I press the "([^"]*)" HTML field button$/
230
     */
231
    public function iClickOnTheHtmlFieldButton($button)
232
    {
233
        $xpath = "//*[@aria-label='" . $button . "']";
234
        $session = $this->getSession();
235
        $element = $session->getPage()->find('xpath', $xpath);
236
        if (null === $element) {
237
            throw new \InvalidArgumentException(sprintf('Could not find element with xpath %s', $xpath));
238
        }
239
240
        $element->click();
241
    }
242
243
    /*
244
	 * @example Given the CMS settings has the following data
245
	 *	| Title | My site title |
246
	 *	| Theme | My site theme |
247
	 * @Given /^the CMS settings have the following data$/
248
	 */
249
    public function theCmsSettingsHasData(TableNode $fieldsTable)
250
    {
251
        $fields = $fieldsTable->getRowsHash();
252
        $siteConfig = SiteConfig::get()->first();
253
        foreach ($fields as $field => $value) {
254
            $siteConfig->$field = $value;
255
        }
256
        $siteConfig->write();
257
        $siteConfig->flushCache();
258
    }
259
260
    /**
261
     * Select a value in the tree dropdown field
262
     *
263
     * NOTE: This is react specific, may need to move to its own react section later
264
     *
265
     * @When /^I select "([^"]*)" in the "([^"]*)" tree dropdown$/
266
     */
267
    public function iSelectValueInTreeDropdown($text, $selector)
268
    {
269
        $page = $this->getSession()->getPage();
270
        $parentElement = null;
271
        $this->retryThrowable(function () use (&$parentElement, &$page, $selector) {
272
            $parentElement = $page->find('css', $selector);
273
            assertNotNull($parentElement, sprintf('"%s" element not found', $selector));
274
            $page = $this->getSession()->getPage();
275
        });
276
277
        $this->retryThrowable(function () use ($parentElement, $selector) {
278
            $dropdown = $parentElement->find('css', '.Select-arrow');
279
            assertNotNull($dropdown, sprintf('Unable to find the dropdown in "%s"', $selector));
280
            $dropdown->click();
281
        });
282
283
        $this->retryThrowable(function () use ($text, $parentElement, $selector) {
284
            $element = $parentElement->find('xpath', sprintf('//*[count(*)=0 and contains(.,"%s")]', $text));
285
            assertNotNull($element, sprintf('"%s" not found in "%s"', $text, $selector));
286
            $element->click();
287
        });
288
    }
289
290
    /**
291
     * Locate an HTML editor field
292
     *
293
     * @param string $locator Raw html field identifier as passed from
294
     * @return NodeElement
295
     */
296
    protected function getHtmlField($locator)
297
    {
298
        $locator = $this->fixStepArgument($locator);
299
        $page = $this->getSession()->getPage();
300
        $element = $page->find('css', 'textarea.htmleditor[name=\'' . $locator . '\']');
301
        assertNotNull($element, sprintf('HTML field "%s" not found', $locator));
302
        return $element;
303
    }
304
305
    /**
306
     * @Given /^the "([^"]*)" field ((?:does not have)|(?:has)) property "([^"]*)"$/
307
     */
308
    public function assertTheFieldHasProperty($name, $cond, $property)
309
    {
310
        $name = $this->fixStepArgument($name);
311
        $property = $this->fixStepArgument($property);
312
313
        $context = $this->getMainContext();
314
        $fieldObj = $context->assertSession()->fieldExists($name);
315
316
        // Check property
317
        $hasProperty = $fieldObj->hasAttribute($property);
318
        switch ($cond) {
319
            case 'has':
320
                assert($hasProperty, "Field $name does not have property $property");
321
                break;
322
            case 'does not have':
323
                assert(!$hasProperty, "Field $name should not have property $property");
324
                break;
325
            default:
326
                throw new BadMethodCallException("Invalid condition");
327
        }
328
    }
329
330
    /**
331
     * @When /^I switch to the "([^"]*)" iframe$/
332
     * @param string $id iframe id property
333
     */
334
    public function stepSwitchToTheFrame($id)
335
    {
336
        $this->getMainContext()->getSession()->getDriver()->switchToIFrame($id);
337
    }
338
339
    /**
340
     * @When /^I am not in an iframe$/
341
     */
342
    public function stepSwitchToParentFrame()
343
    {
344
        $this->getMainContext()->getSession()->getDriver()->switchToIFrame(null);
345
    }
346
347
    /**
348
     * @When /^my session expires$/
349
     */
350
    public function stepMySessionExpires()
351
    {
352
        // Destroy cookie to detach session
353
        $this->getMainContext()->getSession()->setCookie('PHPSESSID', null);
354
    }
355
356
    /**
357
     * @When /^I should see the "([^"]*)" button in the "([^"]*)" gridfield for the "([^"]*)" row$/
358
     * @param string $buttonLabel
359
     * @param string $gridFieldName
360
     * @param string $rowName
361
     */
362
    public function assertIShouldSeeTheGridFieldButtonForRow($buttonLabel, $gridFieldName, $rowName)
363
    {
364
        $button = $this->getGridFieldButton($gridFieldName, $rowName, $buttonLabel);
365
        assertNotNull($button, sprintf('Button "%s" not found', $buttonLabel));
0 ignored issues
show
The function assertNotNull was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

365
        /** @scrutinizer ignore-call */ 
366
        assertNotNull($button, sprintf('Button "%s" not found', $buttonLabel));
Loading history...
366
    }
367
368
    /**
369
     * @When /^I should not see the "([^"]*)" button in the "([^"]*)" gridfield for the "([^"]*)" row$/
370
     * @param string $buttonLabel
371
     * @param string $gridFieldName
372
     * @param string $rowName
373
     */
374
    public function assertIShouldNotSeeTheGridFieldButtonForRow($buttonLabel, $gridFieldName, $rowName)
375
    {
376
        $button = $this->getGridFieldButton($gridFieldName, $rowName, $buttonLabel);
377
        assertNull($button, sprintf('Button "%s" found', $buttonLabel));
378
    }
379
380
    /**
381
     * @When /^I click the "([^"]*)" button in the "([^"]*)" gridfield for the "([^"]*)" row$/
382
     * @param string $buttonLabel
383
     * @param string $gridFieldName
384
     * @param string $rowName
385
     */
386
    public function stepIClickTheGridFieldButtonForRow($buttonLabel, $gridFieldName, $rowName)
387
    {
388
        $button = $this->getGridFieldButton($gridFieldName, $rowName, $buttonLabel);
389
        assertNotNull($button, sprintf('Button "%s" not found', $buttonLabel));
0 ignored issues
show
The function assertNotNull was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

389
        /** @scrutinizer ignore-call */ 
390
        assertNotNull($button, sprintf('Button "%s" not found', $buttonLabel));
Loading history...
390
391
        $button->click();
392
    }
393
394
    /**
395
     * Finds a button in the gridfield row
396
     *
397
     * @param $gridFieldName
398
     * @param $rowName
399
     * @param $buttonLabel
400
     * @return $button
401
     */
402
    protected function getGridFieldButton($gridFieldName, $rowName, $buttonLabel)
403
    {
404
        $page = $this->getSession()->getPage();
405
        $gridField = $page->find('xpath', sprintf('//*[@data-name="%s"]', $gridFieldName));
406
        assertNotNull($gridField, sprintf('Gridfield "%s" not found', $gridFieldName));
0 ignored issues
show
The function assertNotNull was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

406
        /** @scrutinizer ignore-call */ 
407
        assertNotNull($gridField, sprintf('Gridfield "%s" not found', $gridFieldName));
Loading history...
407
408
        $name = $gridField->find('xpath', sprintf('//*[count(*)=0 and contains(.,"%s")]', $rowName));
409
        if (!$name) {
410
            return null;
411
        }
412
413
        $button = $name->getParent()->find('xpath', sprintf('//*[@aria-label="%s"]', $buttonLabel));
414
415
        return $button;
416
    }
417
418
    /**
419
     * @When /^I click the "([^"]*)" option in the "([^"]*)" listbox$/
420
     * @param $optionLabel
421
     * @param $fieldName
422
     */
423
    public function stepIClickTheListBoxOption($optionLabel, $fieldName)
424
    {
425
        $page = $this->getSession()->getPage();
426
        $listBox = $page->find('xpath', sprintf('//*[@name="%s[]"]', $fieldName));
427
        assertNotNull($listBox, sprintf('The listbox %s is not found', $fieldName));
0 ignored issues
show
The function assertNotNull was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

427
        /** @scrutinizer ignore-call */ 
428
        assertNotNull($listBox, sprintf('The listbox %s is not found', $fieldName));
Loading history...
428
429
        $option = $listBox->getParent()
430
            ->find('css', '.chosen-choices')
431
            ->find('xpath', sprintf('//*[count(*)=0 and contains(.,"%s")]', $optionLabel));
432
        assertNotNull($option, sprintf('Option %s is not found', $optionLabel));
433
434
        $button = $option->getParent()->find('css', 'a');
435
436
        $button->click();
437
    }
438
}
439