Test Failed
Push — master ( fbae21...57eb49 )
by Robert
19:14
created

PantherDriver::keyDown()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 3
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
/*
5
 * This file is part of the Behat\Mink.
6
 * (c) Robert Freigang <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Behat\Mink\Driver;
13
14
use Behat\Mink\Exception\DriverException;
15
use Behat\Mink\Exception\UnsupportedDriverActionException;
16
use Facebook\WebDriver\Interactions\Internal\WebDriverCoordinates;
17
use Facebook\WebDriver\Interactions\WebDriverActions;
18
use Facebook\WebDriver\Internal\WebDriverLocatable;
19
use Facebook\WebDriver\WebDriverDimension;
20
use Facebook\WebDriver\WebDriverElement;
21
use Facebook\WebDriver\WebDriverHasInputDevices;
22
use Facebook\WebDriver\WebDriverKeys;
23
use Symfony\Component\BrowserKit\Cookie;
24
use Symfony\Component\BrowserKit\Response;
25
use Symfony\Component\DomCrawler\Field\FormField;
26
use Symfony\Component\Panther\Client;
27
use Symfony\Component\Panther\DomCrawler\Crawler;
28
use Symfony\Component\Panther\DomCrawler\Field\ChoiceFormField;
29
use Symfony\Component\Panther\DomCrawler\Field\FileFormField;
30
use Symfony\Component\Panther\DomCrawler\Field\InputFormField;
31
use Symfony\Component\Panther\DomCrawler\Field\TextareaFormField;
32
use Symfony\Component\Panther\DomCrawler\Form;
33
use Symfony\Component\Panther\PantherTestCaseTrait;
34
35
/**
36
 * Symfony2 Panther driver.
37
 *
38
 * @author Robert Freigang <[email protected]>
39
 */
40
class PantherDriver extends CoreDriver
41
{
42
    use PantherTestCaseTrait;
43
44
    /** @var Client */
45
    private $client;
46
47
    /**
48
     * @var Form[]
49
     */
50
    private $forms = array();
51
    private $started = false;
52
    private $removeScriptFromUrl = false;
53
    private $removeHostFromUrl = false;
54
55
    /**
56
     * Initializes Panther driver.
57
     * external_base_uri
58
     * webServerDir PANTHER_WEB_SERVER_DIR
59
     * port PANTHER_WEB_SERVER_PORT
60
     * router PANTHER_WEB_SERVER_ROUTER
61
     *    protected static $defaultOptions = [
62
     *        'webServerDir' => __DIR__.'/../../../../public', // the Flex directory structure
63
     *        'hostname' => '127.0.0.1',
64
     *        'port' => 9080,
65
     *        'router' => '',
66
     *        'external_base_uri' => null,
67
     *    ];
68
     *
69
     * @param string   $clientType BrowserKit client instance
70
     * @param array    $options
71
     * @param array    $kernelOptions
72
     */
73
    public function __construct(
74
        string $clientType = 'panther',
75
        array $options = [],
76
        array $kernelOptions = []
77
    ) {
78
        if ($clientType === 'panther') {
79
            $client = self::createPantherClient($options, $kernelOptions);
80
        } elseif ($clientType === 'goutte') {
81
            $client = self::createGoutteClient($options, $kernelOptions);
82
        } else {
83
            throw new \InvalidArgumentException('$clientType have to be one of panther or goutte');
84
        }
85
86
        $this->client = $client;
87
    }
88
89
    /**
90
     * Returns BrowserKit HTTP client instance.
91
     *
92
     * @return Client
93
     */
94
    public function getClient()
95
    {
96
        return $this->client;
97
    }
98
99
    /**
100
     * Tells driver to remove hostname from URL.
101
     *
102
     * @param Boolean $remove
103
     *
104
     * @deprecated Deprecated as of 1.2, to be removed in 2.0. Pass the base url in the constructor instead.
105
     */
106
    public function setRemoveHostFromUrl($remove = true)
107
    {
108
        @trigger_error(
109
            'setRemoveHostFromUrl() is deprecated as of 1.2 and will be removed in 2.0. Pass the base url in the constructor instead.',
110
            E_USER_DEPRECATED
111
        );
112
        $this->removeHostFromUrl = (bool)$remove;
113
    }
114
115
    /**
116
     * Tells driver to remove script name from URL.
117
     *
118
     * @param Boolean $remove
119
     *
120
     * @deprecated Deprecated as of 1.2, to be removed in 2.0. Pass the base url in the constructor instead.
121
     */
122
    public function setRemoveScriptFromUrl($remove = true)
123
    {
124
        @trigger_error(
125
            'setRemoveScriptFromUrl() is deprecated as of 1.2 and will be removed in 2.0. Pass the base url in the constructor instead.',
126
            E_USER_DEPRECATED
127
        );
128
        $this->removeScriptFromUrl = (bool)$remove;
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134
    public function start()
135
    {
136
        $this->client = self::createPantherClient();
137
        $this->started = true;
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public function isStarted()
144
    {
145
        return $this->started;
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151
    public function stop()
152
    {
153
        $this->reset();
154
        $this->started = false;
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160
    public function reset()
161
    {
162
        // Restarting the client resets the cookies and the history
163
        $this->client->restart();
164
        $this->forms = array();
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
170
    public function visit($url)
171
    {
172
        $this->client->get($this->prepareUrl($url));
173
        $this->forms = array();
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179
    public function getCurrentUrl()
180
    {
181
        return $this->client->getCurrentURL();
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187
    public function reload()
188
    {
189
        $this->client->reload();
190
        $this->forms = array();
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196
    public function forward()
197
    {
198
        $this->client->forward();
199
        $this->forms = array();
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205
    public function back()
206
    {
207
        $this->client->back();
208
        $this->forms = array();
209
    }
210
211
    /**
212
     * {@inheritdoc}
213
     */
214
    public function switchToWindow($name = null)
215
    {
216
        $this->client->switchTo()->window($name);
217
        $this->client->refreshCrawler();
218
    }
219
220
    /**
221
     * {@inheritdoc}
222
     */
223
    public function switchToIFrame($name = null)
224
    {
225
        if (null === $name) {
226
            $this->client->switchTo()->defaultContent();
227
        } else {
228
            $this->client->switchTo()->frame($name);
229
        }
230
        $this->client->refreshCrawler();
231
    }
232
233
    /**
234
     * {@inheritdoc}
235
     */
236
    public function setCookie($name, $value = null)
237
    {
238
        if (null === $value) {
239
            $this->deleteCookie($name);
240
241
            return;
242
        }
243
244
        $jar = $this->client->getCookieJar();
245
        // @see: https://github.com/w3c/webdriver/issues/1238
246
        $jar->set(new Cookie($name, \rawurlencode((string)$value)));
247
    }
248
249
    /**
250
     * Deletes a cookie by name.
251
     *
252
     * @param string $name Cookie name.
253
     */
254
    private function deleteCookie($name)
255
    {
256
        $path = $this->getCookiePath();
257
        $jar = $this->client->getCookieJar();
258
259
        do {
260
            if (null !== $jar->get($name, $path)) {
261
                $jar->expire($name, $path);
262
            }
263
264
            $path = preg_replace('/.$/', '', $path);
265
        } while ($path);
266
    }
267
268
    /**
269
     * Returns current cookie path.
270
     *
271
     * @return string
272
     */
273
    private function getCookiePath()
274
    {
275
        $path = dirname(parse_url($this->getCurrentUrl(), PHP_URL_PATH));
276
277
        if ('\\' === DIRECTORY_SEPARATOR) {
278
            $path = str_replace('\\', '/', $path);
279
        }
280
281
        return $path;
282
    }
283
284
    /**
285
     * {@inheritdoc}
286
     */
287
    public function getCookie($name)
288
    {
289
        $cookies = $this->client->getCookieJar()->all();
290
291
        foreach ($cookies as $cookie) {
292
            if ($cookie->getName() === $name) {
293
                return \urldecode($cookie->getValue());
294
            }
295
        }
296
297
        return null;
298
    }
299
300
    /**
301
     * {@inheritdoc}
302
     */
303
    public function getContent()
304
    {
305
        return $this->getResponse()->getContent();
306
    }
307
308
    /**
309
     * {@inheritdoc}
310
     */
311
    public function getScreenshot($saveAs = null): string
312
    {
313
        return $this->client->takeScreenshot($saveAs);
314
    }
315
316
    /**
317
     * {@inheritdoc}
318
     */
319
    public function getWindowNames()
320
    {
321
        return $this->client->getWindowHandles();
322
    }
323
324
    /**
325
     * {@inheritdoc}
326
     */
327
    public function getWindowName()
328
    {
329
        return $this->client->getWindowHandle();
330
    }
331
332
    /**
333
     * {@inheritdoc}
334
     */
335
    public function isVisible($xpath)
336
    {
337
        return $this->getCrawlerElement($this->getFilteredCrawler($xpath))->isDisplayed();
338
    }
339
340
    /**
341
     * {@inheritdoc}.
342
     */
343
    public function mouseOver($xpath)
344
    {
345
        $this->client->getMouse()->mouseMove($this->toCoordinates($xpath));
346
    }
347
348
    /**
349
     * {@inheritdoc}
350
     */
351
    public function focus($xpath)
352
    {
353
        $this->client->getMouse()->click($this->toCoordinates($xpath));
354
    }
355
356
    /**
357
     * {@inheritdoc}
358
     */
359
    public function keyPress($xpath, $char, $modifier = null)
360
    {
361
        $webDriverActions = $this->getWebDriverActions();
362
        $element = $this->getCrawlerElement($this->getFilteredCrawler($xpath));
363
        $key = $this->geWebDriverKeyValue($char, $modifier);
364
        $webDriverActions->sendKeys($element, $key.WebDriverKeys::NULL)->perform();
365
    }
366
367
    /**
368
     * {@inheritdoc}
369
     */
370
    public function keyDown($xpath, $char, $modifier = null)
371
    {
372
        $webDriverActions = $this->getWebDriverActions();
373
        $element = $this->getCrawlerElement($this->getFilteredCrawler($xpath));
374
        $key = $this->geWebDriverKeyValue($char, $modifier);
375
        $webDriverActions->keyDown($element, $key.WebDriverKeys::NULL)->perform();
376
    }
377
378
    /**
379
     * {@inheritdoc}
380
     */
381
    public function keyUp($xpath, $char, $modifier = null)
382
    {
383
        $webDriverActions = $this->getWebDriverActions();
384
        $element = $this->getCrawlerElement($this->getFilteredCrawler($xpath));
385
        $key = $this->geWebDriverKeyValue($char, $modifier);
386
        $webDriverActions->keyUp($element, $key)->perform();
387
    }
388
389
    /**
390
     * {@inheritdoc}
391
     */
392
    public function isSelected($xpath)
393
    {
394
        return $this->getCrawlerElement($this->getFilteredCrawler($xpath))->isSelected();
395
    }
396
397
    /**
398
     * {@inheritdoc}
399
     */
400
    public function findElementXpaths($xpath)
401
    {
402
        $nodes = $this->getCrawler()->filterXPath($xpath);
403
404
        $elements = array();
405
        foreach ($nodes as $i => $node) {
406
            $elements[] = sprintf('(%s)[%d]', $xpath, $i + 1);
407
        }
408
409
        return $elements;
410
    }
411
412
    /**
413
     * {@inheritdoc}
414
     */
415
    public function getTagName($xpath)
416
    {
417
        return $this->getCrawlerElement($this->getFilteredCrawler($xpath))->getTagName();
418
    }
419
420
    /**
421
     * {@inheritdoc}
422
     */
423
    public function getText($xpath)
424
    {
425
        $text = $this->getFilteredCrawler($xpath)->text();
426
        $text = str_replace("\n", ' ', $text);
427
        $text = preg_replace('/ {2,}/', ' ', $text);
428
429
        return trim($text);
430
    }
431
432
    /**
433
     * {@inheritdoc}
434
     */
435
    public function getHtml($xpath)
436
    {
437
        // cut the tag itself (making innerHTML out of outerHTML)
438
        return preg_replace('/^\<[^\>]+\>|\<[^\>]+\>$/', '', $this->getOuterHtml($xpath));
439
    }
440
441
    /**
442
     * {@inheritdoc}
443
     */
444
    public function getOuterHtml($xpath)
445
    {
446
        $crawler = $this->getFilteredCrawler($xpath);
447
448
        return $crawler->html();
449
    }
450
451
    /**
452
     * {@inheritdoc}
453
     */
454
    public function getAttribute($xpath, $name)
455
    {
456
        $crawler = $this->getFilteredCrawler($xpath);
457
458
        $attribute = $this->getCrawlerElement($crawler)->getAttribute($name);
459
460
        // let's get hacky
461
        if ('' === $attribute) {
462
            $html = \strtolower($crawler->html());
463
            $name = \strtolower($name).'=';
464
            if (0 === \substr_count($html, $name)) {
465
                $attribute = null;
466
            }
467
        }
468
469
        return $attribute;
470
    }
471
472
    /**
473
     * {@inheritdoc}
474
     */
475
    public function getValue($xpath)
476
    {
477
        try {
478
            $formField = $this->getFormField($xpath);
479
            $value = $formField->getValue();
480
            if ('' === $value && $formField instanceof ChoiceFormField) {
481
                $value = null;
482
            } elseif ($formField instanceof ChoiceFormField && !$formField->hasValue()) {
483
                $value = null;
484
            }
485
        } catch (DriverException $e) {
486
            $element = $this->getCrawlerElement($this->getFilteredCrawler($xpath));
487
            $value = $element->getAttribute('value');
488
        }
489
490
         return $value;
491
    }
492
493
    /**
494
     * {@inheritdoc}
495
     */
496
    public function setValue($xpath, $value)
497
    {
498
        try {
499
            $formField = $this->getFormField($xpath);
500
            $formField->setValue($value);
501
        } catch (DriverException $e) {
502
            $element = $this->getCrawlerElement($this->getFilteredCrawler($xpath));
503
            $element->sendKeys($value);
504
        }
505
    }
506
507
    /**
508
     * {@inheritdoc}
509
     */
510
    public function check($xpath)
511
    {
512
        $this->getChoiceFormField($xpath)->tick();
513
    }
514
515
    /**
516
     * {@inheritdoc}
517
     */
518
    public function uncheck($xpath)
519
    {
520
        $this->getChoiceFormField($xpath)->untick();
521
    }
522
523
    /**
524
     * {@inheritdoc}
525
     */
526
    public function selectOption($xpath, $value, $multiple = false)
527
    {
528
        $field = $this->getFormField($xpath);
529
530
        if (!$field instanceof ChoiceFormField) {
531
            throw new DriverException(
532
                sprintf('Impossible to select an option on the element with XPath "%s" as it is not a select or radio input', $xpath)
533
            );
534
        }
535
536
        if ($multiple) {
537
            $oldValue = (array)$field->getValue();
538
            $oldValue[] = $value;
539
            $value = $oldValue;
540
        }
541
542
        $field->select($value);
543
    }
544
545
    /**
546
     * {@inheritdoc}
547
     */
548
    public function click($xpath)
549
    {
550
        $this->client->getMouse()->click($this->toCoordinates($xpath));
551
    }
552
553
    /**
554
     * {@inheritdoc}
555
     */
556
    public function doubleClick($xpath)
557
    {
558
        $this->client->getMouse()->doubleClick($this->toCoordinates($xpath));
559
    }
560
561
    /**
562
     * {@inheritdoc}
563
     */
564
    public function rightClick($xpath)
565
    {
566
        $this->client->getMouse()->contextClick($this->toCoordinates($xpath));
567
    }
568
569
    /**
570
     * {@inheritdoc}
571
     */
572
    public function isChecked($xpath)
573
    {
574
        $element = $this->getCrawlerElement($this->getFilteredCrawler($xpath));
575
        $type = $element->getAttribute('type');
576
577
578
        if ('radio' === $type) {
579
            return (bool) $element->getAttribute('value');
580
        }
581
582
        $choiceFormField = $this->getChoiceFormField($xpath);
583
584
        return $choiceFormField->hasValue();
585
    }
586
587
    /**
588
     * {@inheritdoc}
589
     */
590
    public function attachFile($xpath, $path)
591
    {
592
        $field = $this->getFormField($xpath);
593
594
        if (!$field instanceof FileFormField) {
595
            throw new DriverException(
596
                sprintf('Impossible to attach a file on the element with XPath "%s" as it is not a file input', $xpath)
597
            );
598
        }
599
600
        $field->upload($path);
601
    }
602
603
    /**
604
     * {@inheritdoc}
605
     */
606
    public function dragTo($sourceXpath, $destinationXpath)
607
    {
608
        $webDriverActions = $this->getWebDriverActions();
609
        $source = $this->getCrawlerElement($this->getFilteredCrawler($sourceXpath));
610
        $target = $this->getCrawlerElement($this->getFilteredCrawler($destinationXpath));
611
        $webDriverActions->dragAndDrop($source, $target)->perform();
612
    }
613
614
    /**
615
     * {@inheritdoc}
616
     */
617
    public function executeScript($script)
618
    {
619
        return $this->client->executeScript($script);
620
    }
621
622
    /**
623
     * {@inheritdoc}
624
     */
625
    public function evaluateScript($script)
626
    {
627
        if (0 !== \strpos(trim($script), 'return ')) {
628
            $script = 'return ' . $script;
629
        }
630
631
        return $this->client->executeScript($script);
632
    }
633
634
    /**
635
     * {@inheritdoc}
636
     */
637
    public function wait($timeout, $condition)
638
    {
639
        $script = "return $condition;";
640
        $start = microtime(true);
641
        $end = $start + $timeout / 1000.0;
642
643
        do {
644
            $result = $this->evaluateScript($script);
645
            \usleep(100000);
646
        } while (\microtime(true) < $end && !$result);
647
648
        return (bool) $result;
649
    }
650
651
    /**
652
     * {@inheritdoc}
653
     */
654
    public function resizeWindow($width, $height, $name = null)
655
    {
656
        $size = new WebDriverDimension($width, $height);
657
        $this->client->getWebDriver()->manage()->window()->setSize($size);
658
    }
659
660
    /**
661
     * {@inheritdoc}
662
     */
663
    public function maximizeWindow($name = null)
664
    {
665
        $this->client->getWebDriver()->manage()->window()->maximize();
666
    }
667
668
    /**
669
     * {@inheritdoc}
670
     */
671
    public function submitForm($xpath)
672
    {
673
        $crawler = $this->getFilteredCrawler($xpath);
674
675
        $this->client->submit($crawler->form());
676
        $this->client->refreshCrawler();
677
        // $this->submit($crawler->form());
678
    }
679
680
    /**
681
     * @return Response
682
     *
683
     * @throws DriverException If there is not response yet
684
     */
685
    protected function getResponse()
686
    {
687
        $response = $this->client->getInternalResponse();
688
689
        if (null === $response) {
690
            throw new DriverException('Unable to access the response before visiting a page');
691
        }
692
693
        return $response;
694
    }
695
696
    /**
697
     * Prepares URL for visiting.
698
     * Removes "*.php/" from urls and then passes it to BrowserKitDriver::visit().
699
     *
700
     * @param string $url
701
     *
702
     * @return string
703
     */
704
    protected function prepareUrl($url)
705
    {
706
        $replacement = ($this->removeHostFromUrl ? '' : '$1').($this->removeScriptFromUrl ? '' : '$2');
707
708
        return preg_replace('#(https?\://[^/]+)(/[^/\.]+\.php)?#', $replacement, $url);
709
    }
710
711
    /**
712
     * Returns form field from XPath query.
713
     *
714
     * @param string $xpath
715
     *
716
     * @return FormField
717
     *
718
     * @throws DriverException
719
     */
720
    private function getFormField($xpath)
721
    {
722
        try {
723
            $formField = $this->getChoiceFormField($xpath);
724
        } catch (DriverException $e) {
725
            $formField = null;
726
        }
727
        if (!$formField) {
728
            try {
729
                $formField = $this->getInputFormField($xpath);
730
            } catch (DriverException $e) {
731
                $formField = null;
732
            }
733
        }
734
        if (!$formField) {
735
            try {
736
                $formField = $this->getFileFormField($xpath);
737
            } catch (DriverException $e) {
738
                $formField = null;
739
            }
740
        }
741
        if (!$formField) {
742
            $formField = $this->getTextareaFormField($xpath);
743
        }
744
745
        return $formField;
746
    }
747
748
    /**
749
     * Returns the checkbox field from xpath query, ensuring it is valid.
750
     *
751
     * @param string $xpath
752
     *
753
     * @return ChoiceFormField
754
     *
755
     * @throws DriverException when the field is not a checkbox
756
     */
757
    private function getChoiceFormField($xpath)
758
    {
759
        $element = $this->getCrawlerElement($this->getFilteredCrawler($xpath));
760
        try {
761
            $choiceFormField = new ChoiceFormField($element);
762
        } catch (\LogicException $e) {
763
            throw new DriverException(sprintf('Impossible to check the element with XPath "%s" as it is not a choice form field.', $xpath));
764
        }
765
766
        return $choiceFormField;
767
    }
768
769
    /**
770
     * Returns the input field from xpath query, ensuring it is valid.
771
     *
772
     * @param string $xpath
773
     *
774
     * @return InputFormField
775
     *
776
     * @throws DriverException when the field is not a checkbox
777
     */
778
    private function getInputFormField($xpath)
779
    {
780
        $element = $this->getCrawlerElement($this->getFilteredCrawler($xpath));
781
        try {
782
            $inputFormField = new InputFormField($element);
783
        } catch (\LogicException $e) {
784
            throw new DriverException(sprintf('Impossible to check the element with XPath "%s" as it is not an input form field.', $xpath));
785
        }
786
787
        return $inputFormField;
788
    }
789
790
    /**
791
     * Returns the input field from xpath query, ensuring it is valid.
792
     *
793
     * @param string $xpath
794
     *
795
     * @return FileFormField
796
     *
797
     * @throws DriverException when the field is not a checkbox
798
     */
799
    private function getFileFormField($xpath)
800
    {
801
        $element = $this->getCrawlerElement($this->getFilteredCrawler($xpath));
802
        try {
803
            $fileFormField = new FileFormField($element);
804
        } catch (\LogicException $e) {
805
            throw new DriverException(sprintf('Impossible to check the element with XPath "%s" as it is not a file form field.', $xpath));
806
        }
807
808
        return $fileFormField;
809
    }
810
811
    /**
812
     * Returns the textarea field from xpath query, ensuring it is valid.
813
     *
814
     * @param string $xpath
815
     *
816
     * @return TextareaFormField
817
     *
818
     * @throws DriverException when the field is not a checkbox
819
     */
820
    private function getTextareaFormField($xpath)
821
    {
822
        $element = $this->getCrawlerElement($this->getFilteredCrawler($xpath));
823
        try {
824
            $textareaFormField = new TextareaFormField($element);
825
        } catch (\LogicException $e) {
826
            throw new DriverException(sprintf('Impossible to check the element with XPath "%s" as it is not a textarea.', $xpath));
827
        }
828
829
        return $textareaFormField;
830
    }
831
832
    /**
833
     * Returns WebDriverElement from crawler instance.
834
     *
835
     * @param Crawler $crawler
836
     *
837
     * @return WebDriverElement
838
     *
839
     * @throws DriverException when the node does not exist
840
     */
841
    private function getCrawlerElement(Crawler $crawler): WebDriverElement
842
    {
843
        $node = $crawler->getElement(0);
844
845
        if (null !== $node) {
846
            return $node;
847
        }
848
849
        throw new DriverException('The element does not exist');
850
    }
851
852
    /**
853
     * Returns a crawler filtered for the given XPath, requiring at least 1 result.
854
     *
855
     * @param string $xpath
856
     *
857
     * @return Crawler
858
     *
859
     * @throws DriverException when no matching elements are found
860
     */
861
    private function getFilteredCrawler($xpath): Crawler
862
    {
863
        if (!count($crawler = $this->getCrawler()->filterXPath($xpath))) {
864
            throw new DriverException(sprintf('There is no element matching XPath "%s"', $xpath));
865
        }
866
867
        return $crawler;
868
    }
869
870
    /**
871
     * Returns crawler instance (got from client).
872
     *
873
     * @return Crawler
874
     *
875
     * @throws DriverException
876
     */
877
    private function getCrawler(): Crawler
878
    {
879
        $crawler = $this->client->getCrawler();
880
881
        if (null === $crawler) {
882
            throw new DriverException('Unable to access the response content before visiting a page');
883
        }
884
885
        return $crawler;
886
    }
887
888
    private function toCoordinates(string $xpath): WebDriverCoordinates
889
    {
890
        $element = $this->getCrawlerElement($this->getFilteredCrawler($xpath));
891
892
        if (!$element instanceof WebDriverLocatable) {
893
            throw new \RuntimeException(
894
                sprintf('The element of "%s" xpath selector does not implement "%s".', $xpath, WebDriverLocatable::class)
895
            );
896
        }
897
898
        return $element->getCoordinates();
899
    }
900
901
    private function getWebDriverActions(): WebDriverActions
902
    {
903
        $webDriver = $this->client->getWebDriver();
904
        if (!$webDriver instanceof WebDriverHasInputDevices) {
905
            throw new UnsupportedDriverActionException('Mouse manipulations are not supported by %s', $this);
906
        }
907
        $webDriverActions = new WebDriverActions($webDriver);
908
909
        return $webDriverActions;
910
    }
911
912
    private function geWebDriverKeyValue($char, $modifier = null)
913
    {
914
        if (\is_int($char)) {
915
            $char = \strtolower(\chr($char));
916
        }
917
918
        switch ($modifier) {
919
            case 'alt':
920
                return WebDriverKeys::ALT.$char;
921
            case 'ctrl':
922
                return WebDriverKeys::CONTROL.$char;
923
            case 'shift':
924
                return WebDriverKeys::SHIFT.$char;
925
            case 'meta':
926
                return WebDriverKeys::META.$char;
927
            case null:
928
            default:
929
                return $char;
930
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
931
        }
932
    }
933
}
934