Mercurial > hg > isophonics-drupal-site
comparison core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 1fec387a4317 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\FunctionalJavascriptTests; | |
4 | |
5 use Behat\Mink\Element\NodeElement; | |
6 use Behat\Mink\Exception\ElementHtmlException; | |
7 use Behat\Mink\Exception\ElementNotFoundException; | |
8 use Behat\Mink\Exception\UnsupportedDriverActionException; | |
9 use Drupal\Tests\WebAssert; | |
10 | |
11 /** | |
12 * Defines a class with methods for asserting presence of elements during tests. | |
13 */ | |
14 class JSWebAssert extends WebAssert { | |
15 | |
16 /** | |
17 * Waits for AJAX request to be completed. | |
18 * | |
19 * @param int $timeout | |
20 * (Optional) Timeout in milliseconds, defaults to 10000. | |
21 * @param string $message | |
22 * (optional) A message for exception. | |
23 * | |
24 * @throws \RuntimeException | |
25 * When the request is not completed. If left blank, a default message will | |
26 * be displayed. | |
27 */ | |
28 public function assertWaitOnAjaxRequest($timeout = 10000, $message = 'Unable to complete AJAX request.') { | |
29 $condition = <<<JS | |
30 (function() { | |
31 function isAjaxing(instance) { | |
32 return instance && instance.ajaxing === true; | |
33 } | |
34 return ( | |
35 // Assert no AJAX request is running (via jQuery or Drupal) and no | |
36 // animation is running. | |
37 (typeof jQuery === 'undefined' || (jQuery.active === 0 && jQuery(':animated').length === 0)) && | |
38 (typeof Drupal === 'undefined' || typeof Drupal.ajax === 'undefined' || !Drupal.ajax.instances.some(isAjaxing)) | |
39 ); | |
40 }()); | |
41 JS; | |
42 $result = $this->session->wait($timeout, $condition); | |
43 if (!$result) { | |
44 throw new \RuntimeException($message); | |
45 } | |
46 } | |
47 | |
48 /** | |
49 * Waits for the specified selector and returns it when available. | |
50 * | |
51 * @param string $selector | |
52 * The selector engine name. See ElementInterface::findAll() for the | |
53 * supported selectors. | |
54 * @param string|array $locator | |
55 * The selector locator. | |
56 * @param int $timeout | |
57 * (Optional) Timeout in milliseconds, defaults to 10000. | |
58 * | |
59 * @return \Behat\Mink\Element\NodeElement|null | |
60 * The page element node if found, NULL if not. | |
61 * | |
62 * @see \Behat\Mink\Element\ElementInterface::findAll() | |
63 */ | |
64 public function waitForElement($selector, $locator, $timeout = 10000) { | |
65 $page = $this->session->getPage(); | |
66 | |
67 $result = $page->waitFor($timeout / 1000, function () use ($page, $selector, $locator) { | |
68 return $page->find($selector, $locator); | |
69 }); | |
70 | |
71 return $result; | |
72 } | |
73 | |
74 /** | |
75 * Waits for the specified selector and returns it when available and visible. | |
76 * | |
77 * @param string $selector | |
78 * The selector engine name. See ElementInterface::findAll() for the | |
79 * supported selectors. | |
80 * @param string|array $locator | |
81 * The selector locator. | |
82 * @param int $timeout | |
83 * (Optional) Timeout in milliseconds, defaults to 10000. | |
84 * | |
85 * @return \Behat\Mink\Element\NodeElement|null | |
86 * The page element node if found and visible, NULL if not. | |
87 * | |
88 * @see \Behat\Mink\Element\ElementInterface::findAll() | |
89 */ | |
90 public function waitForElementVisible($selector, $locator, $timeout = 10000) { | |
91 $page = $this->session->getPage(); | |
92 | |
93 $result = $page->waitFor($timeout / 1000, function () use ($page, $selector, $locator) { | |
94 $element = $page->find($selector, $locator); | |
95 if (!empty($element) && $element->isVisible()) { | |
96 return $element; | |
97 } | |
98 return NULL; | |
99 }); | |
100 | |
101 return $result; | |
102 } | |
103 /** | |
104 * Waits for a button (input[type=submit|image|button|reset], button) with | |
105 * specified locator and returns it. | |
106 * | |
107 * @param string $locator | |
108 * The button ID, value or alt string. | |
109 * @param int $timeout | |
110 * (Optional) Timeout in milliseconds, defaults to 10000. | |
111 * | |
112 * @return \Behat\Mink\Element\NodeElement|null | |
113 * The page element node if found, NULL if not. | |
114 */ | |
115 public function waitForButton($locator, $timeout = 10000) { | |
116 return $this->waitForElement('named', ['button', $locator], $timeout); | |
117 } | |
118 | |
119 /** | |
120 * Waits for a link with specified locator and returns it when available. | |
121 * | |
122 * @param string $locator | |
123 * The link ID, title, text or image alt. | |
124 * @param int $timeout | |
125 * (Optional) Timeout in milliseconds, defaults to 10000. | |
126 * | |
127 * @return \Behat\Mink\Element\NodeElement|null | |
128 * The page element node if found, NULL if not. | |
129 */ | |
130 public function waitForLink($locator, $timeout = 10000) { | |
131 return $this->waitForElement('named', ['link', $locator], $timeout); | |
132 } | |
133 | |
134 /** | |
135 * Waits for a field with specified locator and returns it when available. | |
136 * | |
137 * @param string $locator | |
138 * The input ID, name or label for the field (input, textarea, select). | |
139 * @param int $timeout | |
140 * (Optional) Timeout in milliseconds, defaults to 10000. | |
141 * | |
142 * @return \Behat\Mink\Element\NodeElement|null | |
143 * The page element node if found, NULL if not. | |
144 */ | |
145 public function waitForField($locator, $timeout = 10000) { | |
146 return $this->waitForElement('named', ['field', $locator], $timeout); | |
147 } | |
148 | |
149 /** | |
150 * Waits for an element by its id and returns it when available. | |
151 * | |
152 * @param string $id | |
153 * The element ID. | |
154 * @param int $timeout | |
155 * (Optional) Timeout in milliseconds, defaults to 10000. | |
156 * | |
157 * @return \Behat\Mink\Element\NodeElement|null | |
158 * The page element node if found, NULL if not. | |
159 */ | |
160 public function waitForId($id, $timeout = 10000) { | |
161 return $this->waitForElement('named', ['id', $id], $timeout); | |
162 } | |
163 | |
164 /** | |
165 * Waits for the jQuery autocomplete delay duration. | |
166 * | |
167 * @see https://api.jqueryui.com/autocomplete/#option-delay | |
168 */ | |
169 public function waitOnAutocomplete() { | |
170 // Wait for the autocomplete to be visible. | |
171 return $this->waitForElementVisible('css', '.ui-autocomplete li'); | |
172 } | |
173 | |
174 /** | |
175 * Test that a node, or it's specific corner, is visible in the viewport. | |
176 * | |
177 * Note: Always set the viewport size. This can be done with a PhantomJS | |
178 * startup parameter or in your test with \Behat\Mink\Session->resizeWindow(). | |
179 * Drupal CI Javascript tests by default use a viewport of 1024x768px. | |
180 * | |
181 * @param string $selector_type | |
182 * The element selector type (CSS, XPath). | |
183 * @param string|array $selector | |
184 * The element selector. Note: the first found element is used. | |
185 * @param bool|string $corner | |
186 * (Optional) The corner to test: | |
187 * topLeft, topRight, bottomRight, bottomLeft. | |
188 * Or FALSE to check the complete element (default). | |
189 * @param string $message | |
190 * (optional) A message for the exception. | |
191 * | |
192 * @throws \Behat\Mink\Exception\ElementHtmlException | |
193 * When the element doesn't exist. | |
194 * @throws \Behat\Mink\Exception\ElementNotFoundException | |
195 * When the element is not visible in the viewport. | |
196 */ | |
197 public function assertVisibleInViewport($selector_type, $selector, $corner = FALSE, $message = 'Element is not visible in the viewport.') { | |
198 $node = $this->session->getPage()->find($selector_type, $selector); | |
199 if ($node === NULL) { | |
200 if (is_array($selector)) { | |
201 $selector = implode(' ', $selector); | |
202 } | |
203 throw new ElementNotFoundException($this->session->getDriver(), 'element', $selector_type, $selector); | |
204 } | |
205 | |
206 // Check if the node is visible on the page, which is a prerequisite of | |
207 // being visible in the viewport. | |
208 if (!$node->isVisible()) { | |
209 throw new ElementHtmlException($message, $this->session->getDriver(), $node); | |
210 } | |
211 | |
212 $result = $this->checkNodeVisibilityInViewport($node, $corner); | |
213 | |
214 if (!$result) { | |
215 throw new ElementHtmlException($message, $this->session->getDriver(), $node); | |
216 } | |
217 } | |
218 | |
219 /** | |
220 * Test that a node, or its specific corner, is not visible in the viewport. | |
221 * | |
222 * Note: the node should exist in the page, otherwise this assertion fails. | |
223 * | |
224 * @param string $selector_type | |
225 * The element selector type (CSS, XPath). | |
226 * @param string|array $selector | |
227 * The element selector. Note: the first found element is used. | |
228 * @param bool|string $corner | |
229 * (Optional) Corner to test: topLeft, topRight, bottomRight, bottomLeft. | |
230 * Or FALSE to check the complete element (default). | |
231 * @param string $message | |
232 * (optional) A message for the exception. | |
233 * | |
234 * @throws \Behat\Mink\Exception\ElementHtmlException | |
235 * When the element doesn't exist. | |
236 * @throws \Behat\Mink\Exception\ElementNotFoundException | |
237 * When the element is not visible in the viewport. | |
238 * | |
239 * @see \Drupal\FunctionalJavascriptTests\JSWebAssert::assertVisibleInViewport() | |
240 */ | |
241 public function assertNotVisibleInViewport($selector_type, $selector, $corner = FALSE, $message = 'Element is visible in the viewport.') { | |
242 $node = $this->session->getPage()->find($selector_type, $selector); | |
243 if ($node === NULL) { | |
244 if (is_array($selector)) { | |
245 $selector = implode(' ', $selector); | |
246 } | |
247 throw new ElementNotFoundException($this->session->getDriver(), 'element', $selector_type, $selector); | |
248 } | |
249 | |
250 $result = $this->checkNodeVisibilityInViewport($node, $corner); | |
251 | |
252 if ($result) { | |
253 throw new ElementHtmlException($message, $this->session->getDriver(), $node); | |
254 } | |
255 } | |
256 | |
257 /** | |
258 * Check the visibility of a node, or it's specific corner. | |
259 * | |
260 * @param \Behat\Mink\Element\NodeElement $node | |
261 * A valid node. | |
262 * @param bool|string $corner | |
263 * (Optional) Corner to test: topLeft, topRight, bottomRight, bottomLeft. | |
264 * Or FALSE to check the complete element (default). | |
265 * | |
266 * @return bool | |
267 * Returns TRUE if the node is visible in the viewport, FALSE otherwise. | |
268 * | |
269 * @throws \Behat\Mink\Exception\UnsupportedDriverActionException | |
270 * When an invalid corner specification is given. | |
271 */ | |
272 private function checkNodeVisibilityInViewport(NodeElement $node, $corner = FALSE) { | |
273 $xpath = $node->getXpath(); | |
274 | |
275 // Build the Javascript to test if the complete element or a specific corner | |
276 // is in the viewport. | |
277 switch ($corner) { | |
278 case 'topLeft': | |
279 $test_javascript_function = <<<JS | |
280 function t(r, lx, ly) { | |
281 return ( | |
282 r.top >= 0 && | |
283 r.top <= ly && | |
284 r.left >= 0 && | |
285 r.left <= lx | |
286 ) | |
287 } | |
288 JS; | |
289 break; | |
290 | |
291 case 'topRight': | |
292 $test_javascript_function = <<<JS | |
293 function t(r, lx, ly) { | |
294 return ( | |
295 r.top >= 0 && | |
296 r.top <= ly && | |
297 r.right >= 0 && | |
298 r.right <= lx | |
299 ); | |
300 } | |
301 JS; | |
302 break; | |
303 | |
304 case 'bottomRight': | |
305 $test_javascript_function = <<<JS | |
306 function t(r, lx, ly) { | |
307 return ( | |
308 r.bottom >= 0 && | |
309 r.bottom <= ly && | |
310 r.right >= 0 && | |
311 r.right <= lx | |
312 ); | |
313 } | |
314 JS; | |
315 break; | |
316 | |
317 case 'bottomLeft': | |
318 $test_javascript_function = <<<JS | |
319 function t(r, lx, ly) { | |
320 return ( | |
321 r.bottom >= 0 && | |
322 r.bottom <= ly && | |
323 r.left >= 0 && | |
324 r.left <= lx | |
325 ); | |
326 } | |
327 JS; | |
328 break; | |
329 | |
330 case FALSE: | |
331 $test_javascript_function = <<<JS | |
332 function t(r, lx, ly) { | |
333 return ( | |
334 r.top >= 0 && | |
335 r.left >= 0 && | |
336 r.bottom <= ly && | |
337 r.right <= lx | |
338 ); | |
339 } | |
340 JS; | |
341 break; | |
342 | |
343 // Throw an exception if an invalid corner parameter is given. | |
344 default: | |
345 throw new UnsupportedDriverActionException($corner, $this->session->getDriver()); | |
346 } | |
347 | |
348 // Build the full Javascript test. The shared logic gets the corner | |
349 // specific test logic injected. | |
350 $full_javascript_visibility_test = <<<JS | |
351 (function(t){ | |
352 var w = window, | |
353 d = document, | |
354 e = d.documentElement, | |
355 n = d.evaluate("$xpath", d, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue, | |
356 r = n.getBoundingClientRect(), | |
357 lx = (w.innerWidth || e.clientWidth), | |
358 ly = (w.innerHeight || e.clientHeight); | |
359 | |
360 return t(r, lx, ly); | |
361 }($test_javascript_function)); | |
362 JS; | |
363 | |
364 // Check the visibility by injecting and executing the full Javascript test | |
365 // script in the page. | |
366 return $this->session->evaluateScript($full_javascript_visibility_test); | |
367 } | |
368 | |
369 } |