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 Symfony\Component\DomCrawler\Crawler; |
11
|
|
|
use Behat\Mink\Element\NodeElement; |
12
|
|
|
use SilverStripe\SiteConfig\SiteConfig; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* CmsFormsContext |
16
|
|
|
* |
17
|
|
|
* Context used to define steps related to forms inside CMS. |
18
|
|
|
*/ |
19
|
|
|
class CmsFormsContext implements Context |
20
|
|
|
{ |
21
|
|
|
use MainContextAwareTrait; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Get Mink session from MinkContext |
25
|
|
|
*/ |
26
|
|
|
public function getSession($name = null) |
27
|
|
|
{ |
28
|
|
|
return $this->getMainContext()->getSession($name); |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Returns fixed step argument (with \\" replaced back to "). |
33
|
|
|
* Copied from {@see MinkContext} |
34
|
|
|
* |
35
|
|
|
* @param string $argument |
36
|
|
|
* @return string |
37
|
|
|
*/ |
38
|
|
|
protected function fixStepArgument($argument) |
39
|
|
|
{ |
40
|
|
|
return str_replace('\\"', '"', $argument); |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @Then /^I should( not? |\s*)see an edit page form$/ |
45
|
|
|
*/ |
46
|
|
|
public function stepIShouldSeeAnEditPageForm($negative) |
47
|
|
|
{ |
48
|
|
|
$page = $this->getSession()->getPage(); |
49
|
|
|
|
50
|
|
|
$form = $page->find('css', '#Form_EditForm'); |
51
|
|
|
if (trim($negative)) { |
52
|
|
|
assertNull($form, 'I should not see an edit page form'); |
53
|
|
|
} else { |
54
|
|
|
assertNotNull($form, 'I should see an edit page form'); |
55
|
|
|
} |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @When /^I fill in the "(?P<field>(?:[^"]|\\")*)" HTML field with "(?P<value>(?:[^"]|\\")*)"$/ |
60
|
|
|
* @When /^I fill in "(?P<value>(?:[^"]|\\")*)" for the "(?P<field>(?:[^"]|\\")*)" HTML field$/ |
61
|
|
|
*/ |
62
|
|
View Code Duplication |
public function stepIFillInTheHtmlFieldWith($field, $value) |
|
|
|
|
63
|
|
|
{ |
64
|
|
|
$inputField = $this->getHtmlField($field); |
65
|
|
|
$value = $this->fixStepArgument($value); |
66
|
|
|
|
67
|
|
|
$this->getSession()->evaluateScript(sprintf( |
68
|
|
|
"jQuery('#%s').entwine('ss').getEditor().setContent('%s')", |
69
|
|
|
$inputField->getAttribute('id'), |
70
|
|
|
addcslashes($value, "'") |
71
|
|
|
)); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* @When /^I append "(?P<value>(?:[^"]|\\")*)" to the "(?P<field>(?:[^"]|\\")*)" HTML field$/ |
76
|
|
|
*/ |
77
|
|
View Code Duplication |
public function stepIAppendTotheHtmlField($field, $value) |
|
|
|
|
78
|
|
|
{ |
79
|
|
|
$inputField = $this->getHtmlField($field); |
80
|
|
|
$value = $this->fixStepArgument($value); |
81
|
|
|
|
82
|
|
|
$this->getSession()->evaluateScript(sprintf( |
83
|
|
|
"jQuery('#%s').entwine('ss').getEditor().insertContent('%s')", |
84
|
|
|
$inputField->getAttribute('id'), |
85
|
|
|
addcslashes($value, "'") |
86
|
|
|
)); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @Then /^the "(?P<locator>(?:[^"]|\\")*)" HTML field should(?P<negative> not? |\s*)contain "(?P<html>.*)"$/ |
91
|
|
|
*/ |
92
|
|
|
public function theHtmlFieldShouldContain($locator, $negative, $html) |
93
|
|
|
{ |
94
|
|
|
$element = $this->getHtmlField($locator); |
95
|
|
|
$actual = $element->getValue(); |
96
|
|
|
$regex = '/'.preg_quote($html, '/').'/ui'; |
97
|
|
|
$failed = false; |
98
|
|
|
|
99
|
|
|
if (trim($negative)) { |
100
|
|
|
if (preg_match($regex, $actual)) { |
101
|
|
|
$failed = true; |
102
|
|
|
} |
103
|
|
|
} else { |
104
|
|
|
if (!preg_match($regex, $actual)) { |
105
|
|
|
$failed = true; |
106
|
|
|
} |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
if ($failed) { |
110
|
|
|
$message = sprintf( |
111
|
|
|
'The string "%s" should%sbe found in the HTML of the element matching name "%s". Actual content: "%s"', |
112
|
|
|
$html, |
113
|
|
|
$negative, |
114
|
|
|
$locator, |
115
|
|
|
$actual |
116
|
|
|
); |
117
|
|
|
throw new ElementHtmlException($message, $this->getSession(), $element); |
|
|
|
|
118
|
|
|
} |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
// @codingStandardsIgnoreStart |
122
|
|
|
/** |
123
|
|
|
* Checks formatting in the HTML field, by analyzing the HTML node surrounding |
124
|
|
|
* the text for certain properties. |
125
|
|
|
* |
126
|
|
|
* Example: Given "my text" in the "Content" HTML field should be right aligned |
127
|
|
|
* Example: Given "my text" in the "Content" HTML field should not be bold |
128
|
|
|
* |
129
|
|
|
* @todo Use an actual DOM parser for more accurate assertions |
130
|
|
|
* |
131
|
|
|
* @Given /^"(?P<text>([^"]*))" in the "(?P<field>(?:[^"]|\\")*)" HTML field should(?P<negate>(?: not)?) be (?P<formatting>(.*))$/ |
132
|
|
|
*/ |
133
|
|
|
public function stepContentInHtmlFieldShouldHaveFormatting($text, $field, $negate, $formatting) { |
134
|
|
|
$inputField = $this->getHtmlField($field); |
135
|
|
|
|
136
|
|
|
$crawler = new Crawler($inputField->getValue()); |
137
|
|
|
$matchedNode = null; |
138
|
|
|
foreach($crawler->filterXPath('//*') as $node) { |
139
|
|
|
if( |
140
|
|
|
$node->firstChild |
141
|
|
|
&& $node->firstChild->nodeType == XML_TEXT_NODE |
142
|
|
|
&& stripos($node->firstChild->nodeValue, $text) !== FALSE |
143
|
|
|
) { |
144
|
|
|
$matchedNode = $node; |
145
|
|
|
} |
146
|
|
|
} |
147
|
|
|
assertNotNull($matchedNode); |
148
|
|
|
|
149
|
|
|
$assertFn = $negate ? 'assertNotEquals' : 'assertEquals'; |
150
|
|
|
if($formatting == 'bold') { |
151
|
|
|
call_user_func($assertFn, 'strong', $matchedNode->nodeName); |
152
|
|
|
} else if($formatting == 'left aligned') { |
153
|
|
|
if($matchedNode->getAttribute('style')) { |
154
|
|
|
call_user_func($assertFn, 'text-align: left;', $matchedNode->getAttribute('style')); |
155
|
|
|
} |
156
|
|
|
} else if($formatting == 'right aligned') { |
157
|
|
|
call_user_func($assertFn, 'text-align: right;', $matchedNode->getAttribute('style')); |
158
|
|
|
} |
159
|
|
|
} |
160
|
|
|
// @codingStandardsIgnoreEnd |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Selects the first textual match in the HTML editor. Does not support |
164
|
|
|
* selection across DOM node boundaries. |
165
|
|
|
* |
166
|
|
|
* @When /^I select "(?P<text>([^"]*))" in the "(?P<field>(?:[^"]|\\")*)" HTML field$/ |
167
|
|
|
*/ |
168
|
|
|
public function stepIHighlightTextInHtmlField($text, $field) |
169
|
|
|
{ |
170
|
|
|
$inputField = $this->getHtmlField($field); |
171
|
|
|
$inputFieldId = $inputField->getAttribute('id'); |
172
|
|
|
$text = addcslashes($text, "'"); |
173
|
|
|
|
174
|
|
|
$js = <<<JS |
175
|
|
|
// TODO <IE9 support |
176
|
|
|
// TODO Allow text matches across nodes |
177
|
|
|
var editor = jQuery('#$inputFieldId').entwine('ss').getEditor(), |
178
|
|
|
doc = editor.getInstance().getDoc(), |
179
|
|
|
sel = editor.getInstance().selection, |
180
|
|
|
rng = document.createRange(), |
181
|
|
|
matched = false; |
182
|
|
|
|
183
|
|
|
jQuery(doc).find('body *').each(function() { |
184
|
|
|
if(!matched) { |
185
|
|
|
for(var i=0;i<this.childNodes.length;i++) { |
186
|
|
|
if(!matched && this.childNodes[i].nodeValue && this.childNodes[i].nodeValue.match('$text')) { |
187
|
|
|
rng.setStart(this.childNodes[i], this.childNodes[i].nodeValue.indexOf('$text')); |
188
|
|
|
rng.setEnd(this.childNodes[i], this.childNodes[i].nodeValue.indexOf('$text') + '$text'.length); |
189
|
|
|
sel.setRng(rng); |
190
|
|
|
editor.getInstance().nodeChanged(); |
191
|
|
|
matched = true; |
192
|
|
|
break; |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
} |
196
|
|
|
}); |
197
|
|
|
JS; |
198
|
|
|
|
199
|
|
|
$this->getSession()->executeScript($js); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* @Given /^I should( not? |\s*)see a "([^"]*)" field$/ |
204
|
|
|
*/ |
205
|
|
|
public function iShouldSeeAField($negative, $text) |
206
|
|
|
{ |
207
|
|
|
$page = $this->getSession()->getPage(); |
208
|
|
|
$els = $page->findAll('named', array('field', "'$text'")); |
209
|
|
|
$matchedEl = null; |
210
|
|
|
foreach ($els as $el) { |
211
|
|
|
if ($el->isVisible()) { |
212
|
|
|
$matchedEl = $el; |
213
|
|
|
} |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
if (trim($negative)) { |
217
|
|
|
assertNull($matchedEl); |
218
|
|
|
} else { |
219
|
|
|
assertNotNull($matchedEl); |
220
|
|
|
} |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* Click on the element with the provided CSS Selector |
225
|
|
|
* |
226
|
|
|
* @When /^I press the "([^"]*)" HTML field button$/ |
227
|
|
|
*/ |
228
|
|
|
public function iClickOnTheHtmlFieldButton($button) |
229
|
|
|
{ |
230
|
|
|
$xpath = "//*[@aria-label='".$button."']"; |
231
|
|
|
$session = $this->getSession(); |
232
|
|
|
$element = $session->getPage()->find('xpath', $xpath); |
233
|
|
|
if (null === $element) { |
234
|
|
|
throw new \InvalidArgumentException(sprintf('Could not find element with xpath %s', $xpath)); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
$element->click(); |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/* |
241
|
|
|
* @example Given the CMS settings has the following data |
242
|
|
|
* | Title | My site title | |
243
|
|
|
* | Theme | My site theme | |
244
|
|
|
* @Given /^the CMS settings have the following data$/ |
245
|
|
|
*/ |
246
|
|
|
public function theCmsSettingsHasData(TableNode $fieldsTable) |
247
|
|
|
{ |
248
|
|
|
$fields = $fieldsTable->getRowsHash(); |
249
|
|
|
$siteConfig = SiteConfig::get()->first(); |
250
|
|
|
foreach ($fields as $field => $value) { |
251
|
|
|
$siteConfig->$field = $value; |
252
|
|
|
} |
253
|
|
|
$siteConfig->write(); |
254
|
|
|
$siteConfig->flushCache(); |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Locate an HTML editor field |
259
|
|
|
* |
260
|
|
|
* @param string $locator Raw html field identifier as passed from |
261
|
|
|
* @return NodeElement |
262
|
|
|
*/ |
263
|
|
|
protected function getHtmlField($locator) |
264
|
|
|
{ |
265
|
|
|
$locator = $this->fixStepArgument($locator); |
266
|
|
|
$page = $this->getSession()->getPage(); |
267
|
|
|
$element = $page->find('css', 'textarea.htmleditor[name=\'' . $locator . '\']'); |
268
|
|
|
assertNotNull($element, sprintf('HTML field "%s" not found', $locator)); |
269
|
|
|
return $element; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* @Given /^the "([^"]*)" field ((?:does not have)|(?:has)) property "([^"]*)"$/ |
274
|
|
|
*/ |
275
|
|
|
public function assertTheFieldHasProperty($name, $cond, $property) |
276
|
|
|
{ |
277
|
|
|
$name = $this->fixStepArgument($name); |
278
|
|
|
$property = $this->fixStepArgument($property); |
279
|
|
|
|
280
|
|
|
$context = $this->getMainContext(); |
281
|
|
|
$fieldObj = $context->assertSession()->fieldExists($name); |
282
|
|
|
|
283
|
|
|
// Check property |
284
|
|
|
$hasProperty = $fieldObj->hasAttribute($property); |
285
|
|
|
switch ($cond) { |
286
|
|
|
case 'has': |
287
|
|
|
assert($hasProperty, "Field $name does not have property $property"); |
288
|
|
|
break; |
289
|
|
|
case 'does not have': |
290
|
|
|
assert(!$hasProperty, "Field $name should not have property $property"); |
291
|
|
|
break; |
292
|
|
|
default: |
293
|
|
|
throw new BadMethodCallException("Invalid condition"); |
294
|
|
|
} |
295
|
|
|
} |
296
|
|
|
} |
297
|
|
|
|
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.