Completed
Push — master ( afecc6...1c3d75 )
by Sergii
02:31
created

FormContext::selectFromFollowing()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 6
rs 9.4286
cc 2
eloc 3
nc 2
nop 1
1
<?php
2
/**
3
 * @author Sergey Bondarenko, <[email protected]>
4
 */
5
namespace Drupal\TqExtension\Context\Form;
6
7
// Exceptions.
8
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
9
use Behat\Mink\Exception\ElementNotFoundException;
10
use WebDriver\Exception\NoSuchElement;
11
12
// Helpers.
13
use Behat\Gherkin\Node\TableNode;
14
use WebDriver\Service\CurlService;
15
use Behat\Mink\Element\NodeElement;
16
17
// Utils.
18
use Drupal\TqExtension\Utils\DatePicker;
19
use Drupal\TqExtension\Utils\FormValueAssertion;
20
use Drupal\TqExtension\Utils\EntityDrupalWrapper;
21
22
class FormContext extends RawFormContext
23
{
24
    /**
25
     * @param string $value
26
     *   Typed text.
27
     * @param string $selector
28
     *   Selector of the field.
29
     * @param int $option
30
     *   An option number. Will be selected from loaded variants.
31
     *
32
     * @throws \InvalidArgumentException
33
     *   When $option is less than zero.
34
     * @throws NoSuchElement
35
     *   When autocomplete list was not loaded.
36
     * @throws \RuntimeException
37
     *   When neither option was not loaded.
38
     * @throws \OverflowException
39
     *   When $option is more than variants are available.
40
     * @throws \Exception
41
     *   When value was not changed.
42
     *
43
     * @Then /^(?:|I )typed "([^"]*)" in the "([^"]*)" field and chose (\d+) option from autocomplete variants$/
44
     */
45
    public function choseOptionFromAutocompleteVariants($value, $selector, $option)
46
    {
47
        if (!$option) {
48
            throw new \InvalidArgumentException(sprintf(
49
                'An option that will be chosen expected as positive number, but was got the: %s',
50
                $option
51
            ));
52
        }
53
54
        $field = $this->element('field', $selector);
55
        // Syn - a Standalone Synthetic Event Library, provided by Selenium.
56
        $this->executeJsOnElement($field, sprintf("Syn.type({{ELEMENT}}, '%s')", token_replace($value)));
57
        $this->waitAjaxAndAnimations();
58
59
        $autocomplete = $field->getParent()->findById('autocomplete');
60
        $this->throwNoSuchElementException('#autocomplete', $autocomplete);
61
62
        $options = count($autocomplete->findAll('css', 'li'));
63
64
        if ($options < 1) {
65
            throw new \RuntimeException('Neither option was not loaded.');
66
        }
67
68
        if ($option > $options) {
69
            throw new \OverflowException(sprintf(
70
                'You can not select an option %s, as there are only %d.',
71
                $option,
72
                $options
73
            ));
74
        }
75
76
        for ($i = 0; $i < $option; $i++) {
77
            // 40 - down
78
            $field->keyDown(40);
79
            $field->keyUp(40);
80
        }
81
82
        // 13 - return
83
        $field->keyDown(13);
84
        $field->keyUp(13);
85
86
        if ($field->getValue() == $value) {
87
            throw new \Exception(sprintf('The value of "%s" field was not changed.', $selector));
88
        }
89
    }
90
91
    /**
92
     * Use the current user data for filling fields.
93
     *
94
     * @example
95
     * Then I fill "First name" with value of field "First name" of current user
96
     * And fill "field_last_name[und][0]" with value of field "field_user_last_name" of current user
97
     *
98
     * @param string $field
99
     *   The name of field to fill in. HTML Label, name or ID can be user as selector.
100
     * @param string $user_field
101
     *   The name of field from which the data will taken. Drupal label or machine name can be used as selector.
102
     *
103
     * @throws \InvalidArgumentException
104
     * @throws \UnexpectedValueException
105
     * @throws \Exception
106
     * @throws NoSuchElement
107
     *   When field cannot be found.
108
     *
109
     * @Then /^(?:I )fill "([^"]*)" with value of field "([^"]*)" of current user$/
110
     */
111
    public function fillInWithValueOfFieldOfCurrentUser($field, $user_field)
112
    {
113
        if (!empty($this->user) && !$this->user->uid) {
114
            throw new \Exception('Anonymous user have no fields');
115
        }
116
117
        $entity = new EntityDrupalWrapper('user');
118
        $wrapper = $entity->wrapper($this->user->uid);
119
        $user_field = $entity->getFieldNameByLocator($user_field);
120
121
        if (empty($wrapper->{$user_field})) {
122
            throw new \InvalidArgumentException(sprintf('User entity has no "%s" field.', $user_field));
123
        }
124
125
        $value = $wrapper->{$user_field}->value();
126
127
        if (empty($value)) {
128
            throw new \UnexpectedValueException('The value of "%s" field is empty.', $user_field);
129
        }
130
131
        $this->fillField($field, $value);
132
    }
133
134
    /**
135
     * @param string $action
136
     *   Can be "check" or "uncheck".
137
     * @param TableNode $checkboxes
138
     *   Table with one row of checkboxes selectors.
139
     *
140
     * @example
141
     * I uncheck the boxes:
142
     *   | Consumer Products  |
143
     *   | Financial Services |
144
     *
145
     * @example
146
     * I check the boxes:
147
     *   | Consumer Products  |
148
     *   | Financial Services |
149
     *
150
     * @Given /^(?:|I )(?:|un)check the boxes:/
151
     */
152
    public function checkboxAction($action, TableNode $checkboxes)
153
    {
154
        $minkContext = $this->getMinkContext();
155
156
        foreach ($checkboxes->getRows() as $checkbox) {
157
            $minkContext->{trim($action) . 'Option'}(reset($checkbox));
158
        }
159
    }
160
161
    /**
162
     * This method was defined and used instead of "assertSelectRadioById",
163
     * because the field label can contain too long value and better to use
164
     * another selector instead of label.
165
     *
166
     * @see MinkContext::assertSelectRadioById()
167
     *
168
     * @param string $customized
169
     *   Can be an empty string or " customized".
170
     * @param string $selector
171
     *   Field selector.
172
     *
173
     * @throws NoSuchElement
174
     *   When radio button was not found.
175
     * @throws \Exception
176
     *
177
     * @Given /^(?:|I )check the(| customized) "([^"]*)" radio button$/
178
     */
179
    public function radioAction($customized, $selector)
180
    {
181
        $field = $this->getWorkingElement()->findField($selector);
182
        $customized = (bool) $customized;
183
184
        if ($field !== null && !$customized) {
185
            $field->selectOption($field->getAttribute('value'));
186
            return;
187
        }
188
189
        // Find all labels of a radio button or only first, if it is not custom.
190
        foreach ($this->findLabels($selector) as $label) {
191
            // Check a custom label for visibility.
192
            if ($customized && !$label->isVisible()) {
193
                continue;
194
            }
195
196
            $label->click();
197
            return;
198
        }
199
200
        $this->throwNoSuchElementException($selector, $field);
201
    }
202
203
    /**
204
     * @param string $selector
205
     * @param string $value
206
     *
207
     * @throws NoSuchElement
208
     *
209
     * @When /^(?:|I )fill "([^"]*)" with "([^"]*)"$/
210
     */
211
    public function fillField($selector, $value)
212
    {
213
        $this->element('field', $selector)->setValue(token_replace($value));
214
    }
215
216
    /**
217
     * @param TableNode $fields
218
     *   | Field locator | Value |
219
     *
220
     * @throws NoSuchElement
221
     *
222
     * @When /^(?:|I )fill the following:$/
223
     */
224
    public function fillFields(TableNode $fields)
225
    {
226
        foreach ($fields->getRowsHash() as $field => $value) {
227
            $this->fillField($field, $value);
228
        }
229
    }
230
231
    /**
232
     * @param string $file
233
     *   Path to a file. Relative to the directory specified in "files_path" in behat.yml.
234
     * @param string $selector
235
     *   Field selector (label|id|name).
236
     *
237
     * @throws \Exception
238
     * @throws NoSuchElement
239
     *
240
     * @Given /^(?:|I )attach file "([^"]*)" to "([^"]*)"$/
241
     */
242
    public function attachFile($file, $selector)
243
    {
244
        $filesPath = $this->getMinkParameter('files_path');
245
246
        if (!$filesPath) {
247
            throw new \Exception('The "files_path" Mink parameter was not configured.');
248
        }
249
250
        $file = rtrim(realpath($filesPath), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $file;
251
252
        if (!is_file($file)) {
253
            throw new \InvalidArgumentException(sprintf('The "%s" file does not exist.', $file));
254
        }
255
256
        $this->element('field', $selector)->attachFile($file);
257
    }
258
259
    /**
260
     * @param string $selector
261
     * @param TableNode $values
262
     *
263
     * @throws ElementNotFoundException
264
     * @throws \Exception
265
     * @throws NoSuchElement
266
     *
267
     * @Given /^(?:|I )select the following in "([^"]*)" hierarchical select:$/
268
     */
269
    public function setValueForHierarchicalSelect($selector, TableNode $values)
270
    {
271
        $element = $this->getWorkingElement();
272
        // Try to selects by wrapper ID.
273
        $wrapper = $element->findById($selector);
274
275
        if (null !== $wrapper) {
276
            $labels = $wrapper->findAll('xpath', '//label[@for]');
277
        } else {
278
            $labels = $this->findLabels($selector);
279
        }
280
281
        if (empty($labels)) {
282
            throw new \Exception('No one hierarchical select was found.');
283
        }
284
285
        /** @var NodeElement $label */
286
        $label = reset($labels);
287
        $parent = $label->getParent();
288
289
        foreach (array_keys($values->getRowsHash()) as $i => $value) {
290
            /** @var NodeElement[] $selects */
291
            $selects = [];
292
293
            /** @var NodeElement $select */
294
            foreach ($parent->findAll('css', 'select') as $select) {
295
                if ($select->isVisible()) {
296
                    $selects[] = $select;
297
                }
298
            }
299
300
            if (!isset($selects[$i])) {
301
                throw new \InvalidArgumentException(sprintf(
302
                    'The value "%s" was specified for select "%s" but it does not exist.',
303
                    $value,
304
                    $i
305
                ));
306
            }
307
308
            $selects[$i]->selectOption($value);
309
            $this->waitAjaxAndAnimations();
310
        }
311
    }
312
313
    /**
314
     * Check that an image was uploaded and can be viewed on the page.
315
     *
316
     * @throws \Exception
317
     * @throws FileNotFoundException
318
     *
319
     * @Then /^(?:|I )should see the thumbnail$/
320
     */
321
    public function shouldSeeThumbnail()
322
    {
323
        $thumb = false;
324
325
        foreach (['media-thumbnail', 'image-preview'] as $classname) {
326
            if ($thumb) {
327
                break;
328
            }
329
330
            $thumb = $this->findByCss(".$classname img");
331
        }
332
333
        if (!$thumb) {
334
            throw new \Exception('An expected image tag was not found.');
335
        }
336
337
        $file = explode('?', $thumb->getAttribute('src'));
338
        $file = reset($file);
339
340
        $curl = new CurlService();
341
        list(, $info) = $curl->execute('GET', $file);
342
343
        if (empty($info) || strpos($info['content_type'], 'image/') === false) {
344
            throw new FileNotFoundException(sprintf('%s did not return an image', $file));
345
        }
346
    }
347
348
    /**
349
     * Check that the page have no error messages and fields - error classes.
350
     *
351
     * @throws \InvalidArgumentException
352
     * @throws \RuntimeException
353
     * @throws \Exception
354
     *
355
     * @Then /^(?:|I )should see no errors$/
356
     */
357
    public function shouldSeeNoErrors()
358
    {
359
        $selector = $this->getDrupalSelector('error_message_selector');
360
361
        if (empty($selector)) {
362
            throw new \InvalidArgumentException('The "error_message_selector" in behat.yml is not configured.');
363
        }
364
365
        $session = $this->getSession();
366
        $page = $session->getPage();
367
        $errors = $page->find('css', $selector);
368
369
        // Some modules are inserted an empty container for errors before
370
        // they are arise. The "Clientside Validation" - one of them.
371
        if (null !== $errors) {
372
            $text = $errors->getText();
373
374
            if (!empty($text)) {
375
                throw new \RuntimeException(sprintf(
376
                    'The page "%s" contains following error messages: "%s"',
377
                    $session->getCurrentUrl(),
378
                    $text
379
                ));
380
            }
381
        }
382
383
        /** @var NodeElement $formElement */
384
        foreach ($page->findAll('css', 'input, select, textarea') as $formElement) {
385
            if ($formElement->hasClass('error')) {
386
                throw new \Exception(sprintf('Element "#%s" has an error class.', $formElement->getAttribute('id')));
387
            }
388
        }
389
    }
390
391
    /**
392
     * @param string $option
393
     * @param string $selector
394
     *
395
     * @Then /^(?:|I )pick "([^"]*)" from "([^"]*)"$/
396
     */
397
    public function selectFrom($option, $selector)
398
    {
399
        $this->element('*', $selector)->selectOption($option);
400
    }
401
402
    /**
403
     * @example
404
     * And pick the following:
405
     *   | Entity Reference                     | Type of new field    |
406
     *   | Inline entity form - Multiple values | Widget for new field |
407
     *
408
     * @param TableNode $rows
409
     *
410
     * @Then /^(?:|I )pick the following:$/
411
     */
412
    public function selectFromFollowing(TableNode $rows)
413
    {
414
        foreach ($rows->getRowsHash() as $option => $selector) {
415
            $this->selectFrom($option, $selector);
416
        }
417
    }
418
419
    /**
420
     * @example
421
     * And check that "Users" field has "admin" value
422
     * And check that "Users" field has not "customer" value
423
     *
424
     * @Then /^(?:|I )check that "([^"]*)" field has(| not) "([^"]*)" value$/
425
     */
426
    public function assertTextualField($selector, $not, $expected)
427
    {
428
        (new FormValueAssertion($this, $selector, $not, $expected))->textual();
429
    }
430
431
    /**
432
     * @example
433
     * And check that "User" is selected in "Apply to" select
434
     * And check that "Product(s)" is not selected in "Apply to" select
435
     *
436
     * @Then /^(?:|I )check that "([^"]*)" is(| not) selected in "([^"]*)" select$/
437
     */
438
    public function assertSelectableField($expected, $not, $selector)
439
    {
440
        (new FormValueAssertion($this, $selector, $not, $expected))->selectable();
441
    }
442
443
    /**
444
     * @example
445
     * And check that "Order discount" is checked
446
     * And check that "Product discount" is not checked
447
     *
448
     * @Then /^(?:|I )check that "([^"]*)" is(| not) checked$/
449
     */
450
    public function assertCheckableField($selector, $not)
451
    {
452
        (new FormValueAssertion($this, $selector, $not))->checkable();
453
    }
454
455
    /**
456
     * @param string $date
457
     * @param string $selector
458
     *
459
     * @Then /^(?:|I )choose "([^"]*)" in "([^"]*)" datepicker$/
460
     * @Then /^(?:|I )set the "([^"]*)" for "([^"]*)" datepicker$/
461
     */
462
    public function setDate($date, $selector)
463
    {
464
        (new DatePicker($this, $selector, $date))->isDateAvailable()->setDate()->isDateSelected();
465
    }
466
467
    /**
468
     * @param string $selector
469
     * @param string $date
470
     *
471
     * @Then /^(?:|I )check that "([^"]*)" datepicker contains "([^"]*)" date$/
472
     */
473
    public function isDateSelected($selector, $date)
474
    {
475
        (new DatePicker($this, $selector, $date))->isDateSelected();
476
    }
477
478
    /**
479
     * @param string $date
480
     * @param string $selector
481
     *
482
     * @Then /^(?:|I )check that "([^"]*)" is available for "([^"]*)" datepicker$/
483
     */
484
    public function isDateAvailable($date, $selector)
485
    {
486
        (new DatePicker($this, $selector, $date))->isDateAvailable();
487
    }
488
}
489