Completed
Push — develop ( a749c9...6ccf59 )
by Peter
01:43
created

AbstractTestCase::resolveWindowSize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 7
rs 9.4285
1
<?php
2
/**
3
 * Webino (http://webino.sk/)
4
 *
5
 * @link        https://github.com/webino/WebinoDev/ for the canonical source repository
6
 * @copyright   Copyright (c) 2014-2016 Webino, s. r. o. (http://webino.sk/)
7
 * @license     BSD-3-Clause
8
 */
9
10
namespace WebinoDev\Test\Selenium;
11
12
use Exception;
13
use PHPWebDriver_WebDriver;
14
use PHPWebDriver_WebDriverWait as Wait;
15
use PHPWebDriver_WebDriverElement as WebDriverElement;
16
use RuntimeException;
17
18
/**
19
 * Base test case for Selenium WebDriver tests
20
 */
21
abstract class AbstractTestCase extends \PHPUnit_Framework_TestCase
22
{
23
    use ElementTrait;
24
    use ElementsTrait;
25
    use NotifyTrait;
26
    use ScreenshotTrait;
27
28
    /**
29
     * Selenium Web Driver host URI
30
     *
31
     * @var string
32
     */
33
    protected static $webDriverHost = 'http://localhost:%s/wd/hub';
34
35
    /**
36
     * Selenium Web Driver port
37
     *
38
     * @var string
39
     */
40
    protected static $webDriverPort = '4444';
41
42
    /**
43
     * @var string
44
     */
45
    protected static $webDriverBrowser = 'firefox';
46
47
    /**
48
     * @var string
49
     */
50
    protected static $webDriverScreen = '1920x1080';
51
52
    /**
53
     * @var string
54
     */
55
    protected $browser;
56
57
    /**
58
     * @var string
59
     */
60
    protected $browserBin;
61
62
    /**
63
     * @var string
64
     */
65
    protected $uri;
66
67
    /**
68
     * @var PHPWebDriver_WebDriver
69
     */
70
    protected $webDriver;
71
72
    /**
73
     * @var \PHPWebDriver_WebDriverSession|WebDriver\SessionInterface
74
     */
75
    protected $session;
76
77
    /**
78
     * Resolves URI to open session
79
     */
80
    protected function setUp()
81
    {
82
        $this->uri       = $this->resolveUri();
83
        $this->webDriver = new PHPWebDriver_WebDriver($this->resolveHost());
84
        $this->session   = $this->webDriver->session($this->resolveBrowser(), $this->resolveCapabilities());
85
86
        $this->session->window()->postSize($this->resolveWindowSize());
87
    }
88
89
    /**
90
     * @return void
91
     */
92
    protected function tearDown()
93
    {
94
        $this->hasFailed() or $this->session->close();
0 ignored issues
show
Bug introduced by
The method close() does not seem to exist on object<WebinoDev\Test\Se...river\SessionInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
95
    }
96
97
    /**
98
     * @param Exception $exc
99
     * @throws Exception
100
     */
101
    protected function onNotSuccessfulTest(Exception $exc)
102
    {
103
        $this->notifyError($exc);
104
        $this->session->close();
0 ignored issues
show
Bug introduced by
The method close() does not seem to exist on object<WebinoDev\Test\Se...river\SessionInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
105
        parent::onNotSuccessfulTest($exc);
106
    }
107
108
    /**
109
     * @return string
110
     */
111
    protected function getBrowser()
112
    {
113
        return $this->browser ? $this->browser : $this::$webDriverBrowser;
114
    }
115
116
    /**
117
     * @return string
118
     */
119
    protected function getBrowserBin()
120
    {
121
        return $this->browserBin;
122
    }
123
124
    /**
125
     * @param string $bin
126
     * @return string
127
     */
128
    protected function setBrowserBin($bin)
129
    {
130
        $this->browserBin = (string) $bin;
131
        return $this;
132
    }
133
134
    /**
135
     * @return \PHPWebDriver_WebDriverSession|WebDriver\SessionInterface
136
     */
137
    public function getSession()
138
    {
139
        return $this->session;
140
    }
141
142
    /**
143
     * @return string
144
     */
145
    protected function getSessionId()
146
    {
147
        return $this->session->getCookie('PHPSESSID')['value'];
0 ignored issues
show
Bug introduced by
The method getCookie() does not seem to exist on object<WebinoDev\Test\Se...river\SessionInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
148
    }
149
150
    /**
151
     * @return string
152
     */
153
    protected function getServerUrl()
154
    {
155
        $parts = parse_url($this->session->url());
156
        return $parts['scheme'] . '://' . $parts['host'];
157
    }
158
159
    /**
160
     * Get raw source from URL
161
     *
162
     * @param string $url
163
     * @param null $sessId
164
     * @return string
165
     */
166
    protected function source($url, $sessId = null)
167
    {
168
        $sid = $sessId ? $sessId : $this->getSessionId();
169
        $opts = ['http' => ['header' => 'Cookie: PHPSESSID=' . $sid ."\r\n"]];
170
        return file_get_contents($url, false, stream_context_create($opts));
171
    }
172
173
    /**
174
     * Resolve Selenium WebDriver host
175
     *
176
     * @return string
177
     */
178
    protected function resolveHost()
179
    {
180
        $host = getenv('HOST');
181
        empty($host) and $host = $this::$webDriverHost;
182
        return sprintf($host, $this->resolvePort());
183
    }
184
185
    /**
186
     * Resolve Selenium WebDriver port
187
     *
188
     * @return string
189
     */
190
    protected function resolvePort()
191
    {
192
        $port = getenv('PORT');
193
        empty($port) and $port = $this::$webDriverPort;
194
        return $port;
195
    }
196
197
    /**
198
     * Resolve test session browser
199
     *
200
     * @return string
201
     */
202
    protected function resolveBrowser()
203
    {
204
        $browser = getenv('BROWSER');
205
        empty($browser) and $browser = $this::$webDriverBrowser;
206
207
        switch ($browser) {
208
            case 'chromium':
209
                $browser = 'chrome';
210
                $this->setBrowserBin('/usr/bin/chromium-browser');
211
                break;
212
            case 'firefox':
213
                $this->setBrowserBin('/var/lib/webino/firefox/firefox');
214
                break;
215
        }
216
217
        return $this->browser = $browser;
218
    }
219
220
    /**
221
     * Resolve test session capabilities
222
     *
223
     * @return array
224
     */
225
    protected function resolveCapabilities()
226
    {
227
        switch ($this->getBrowser()) {
228
            case 'chrome':
229
                // Fixes OpenVZ
230
                $opts = ['args' => ['no-sandbox', 'start-maximized']];
231
                $bin  = $this->getBrowserBin();
232
                $bin and $opts+= ['binary' => $bin];
233
                return ['chromeOptions' => $opts];
234
            case 'firefox':
235
                $opts = [];
236
                $bin  = $this->getBrowserBin();
237
                $bin and $opts+= ['firefox_binary' => $bin];
238
                return $opts;
239
        }
240
        return [];
241
    }
242
243
    /**
244
     * Resolve test target URI
245
     *
246
     * @return string
247
     */
248
    protected function resolveUri()
249
    {
250
        $uri = getenv('URI');
251
        if (empty($uri)) {
252
            throw new RuntimeException('Expected URI env');
253
        }
254
        return $uri;
255
    }
256
257
    /**
258
     * @return array
259
     */
260
    protected function resolveWindowSize()
261
    {
262
        $screen = getenv('SCREEN');
263
        empty($screen) and $screen = $this::$webDriverScreen;
264
        $size = explode('x', $screen);
265
        return ['width' => (int) $size[0], 'height' => (int) $size[1]];
266
    }
267
268
    /**
269
     * @param float $sec
270
     * @return $this
271
     */
272
    protected function sleep($sec = 2.0)
273
    {
274
        sleep($sec);
275
        return $this;
276
    }
277
278
    /**
279
     * Opens URI and asserts not error
280
     *
281
     * @param string $path
282
     * @param string|null $caption
283
     * @return $this
284
     */
285
    protected function open($path = '', $caption = null)
286
    {
287
        $this->session->open($this->uri . $path);
0 ignored issues
show
Bug introduced by
The method open() does not seem to exist on object<WebinoDev\Test\Se...river\SessionInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
288
        $this->debugNotify($caption);
289
        $this->assertNotError();
290
        return $this;
291
    }
292
293
    /**
294
     * @param string $path
295
     * @param string $caption
296
     * @return $this
297
     * @todo remove
298
     * @deprecated use open()
299
     */
300
    protected function openOk($path = '', $caption = 'Home')
301
    {
302
        return $this->open($path, $caption);
303
    }
304
305
    /**
306
     * Assert that page is without errors
307
     *
308
     * @return $this
309
     */
310
    protected function assertNotError()
311
    {
312
        $this->assertNotContains('Error', $this->session->title());
313
314
        // strip script contents & tags
315
        $text = $this->session->source();
316
        $expr = '~<(script).*?>.*?</script>~si';
317
        $src  = strip_tags(preg_replace($expr, '', $text));
318
319
        $this->assertNotContains('Error', $src);
320
        $this->assertNotContains('Exception', $src);
321
        return $this;
322
    }
323
324
    /**
325
     * @return bool
326
     */
327
    public function is404()
328
    {
329
        $url = $this->session->url();
330
        $headers = get_headers($url);
331
        if (empty($headers[0])) {
332
            return true;
333
        }
334
335
        return 'HTTP/1.0 404 Not Found' === $headers[0];
336
    }
337
338
    /**
339
     * Clicks on a link
340
     *
341
     * @param string $linkText
342
     * @param callable $callback
343
     * @return $this
344
     */
345
    protected function clickLink($linkText, callable $callback = null)
346
    {
347
        $elm = $this->elementByLinkText($linkText);
348
        $elm->click();
349
        $callback and call_user_func($callback, $elm);
350
        return $this;
351
    }
352
353
    /**
354
     * Clicks on ajax-link
355
     *
356
     * @param string $linkText
357
     * @param callable $callback
358
     * @return $this
359
     */
360
    protected function clickAjaxLink($linkText, callable $callback = null)
361
    {
362
        $this->clickLink($linkText, $callback);
363
        $this->waitForAjax();
364
        return $this;
365
    }
366
367
    /**
368
     * @param string $class
369
     * @return $this
370
     */
371
    protected function clickByClass($class)
372
    {
373
        $this->elementByClassName($class)->click();
374
        return $this;
375
    }
376
377
    /**
378
     * @param string $name
379
     * @param string $value
380
     * @return $this
381
     * @deprecated use getSelect()
382
     */
383
    protected function clickSelect($name, $value)
384
    {
385
        $selector = sprintf('select[name="%s"] option[value="%s"]', $name, $value);
386
        $this->elementByCssSelector($selector)->click();
387
        return $this;
388
    }
389
390
    /**
391
     * @param string $name
392
     * @return WebDriver\Select
393
     */
394
    protected function getSelect($name)
395
    {
396
        return new WebDriver\Select($this->elementByName($name));
0 ignored issues
show
Bug introduced by
It seems like $this->elementByName($name) targeting WebinoDev\Test\Selenium\...tTrait::elementByName() can also be of type object<WebinoDev\Test\Se...river\ElementInterface>; however, WebinoDev\Test\Selenium\...r\Select::__construct() does only seem to accept object<PHPWebDriver_WebDriverElement>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
397
    }
398
399
    /**
400
     * Enters the input value
401
     *
402
     * @param string|WebDriverElement $name
403
     * @param string $value
404
     * @param callable|false|null $callback
405
     * @return $this
406
     */
407
    protected function enterInput($name, $value, $callback = null)
408
    {
409
        $resolveElm = function () use ($name) {
410
            if ($name instanceof WebDriver\ElementInterface
411
                || $name instanceof WebDriverElement
0 ignored issues
show
Bug introduced by
The class PHPWebDriver_WebDriverElement does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
412
            ) {
413
                return $name;
414
            }
415
            return $this->elementByName($name);
416
        };
417
418
        /** @var WebDriverElement|WebDriver\ElementInterface $elm */
419
        $elm = $resolveElm();
420
        if (null === $callback) {
421
            $this->sleep(1);
422
            $elm->clear();
423
            $elm->sendKeys('');
424
            $this->sleep(1);
425
        }
426
        $elm->clear();
427
        $elm->sendKeys($value);
428
        is_callable($callback) and call_user_func($callback, $elm);
429
        return $this;
430
    }
431
432
    /**
433
     * Submit the input value
434
     *
435
     * @param string|WebDriverElement $name
436
     * @param string $value
437
     * @return $this
438
     */
439
    protected function submitInput($name, $value)
440
    {
441
        $this->enterInput($name, $value, function ($elm) {
442
            /** @var $elm WebDriver\ElementInterface */
443
            $elm->submit();
444
        });
445
        return $this;
446
    }
447
448
    /**
449
     * Assert that input value is same than expected
450
     *
451
     * @param string|WebDriverElement|WebDriver\ElementInterface $name
452
     * @param string $expectedValue
453
     * @param callable $callback
454
     * @return $this
455
     */
456
    public function assertInput($name, $expectedValue, callable $callback = null)
457
    {
458
        $elm = ($name instanceof WebDriverElement) ? $name : $this->elementByName($name);
0 ignored issues
show
Bug introduced by
The class PHPWebDriver_WebDriverElement does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
459
        $this->assertSame($expectedValue, $elm->attribute('value'));
460
        $callback and call_user_func($callback, $elm);
461
        return $this;
462
    }
463
464
    /**
465
     * Focus a browser window
466
     *
467
     * @param int $index
468
     * @return $this
469
     */
470
    protected function focusWindow($index)
471
    {
472
        $session = $this->getSession();
473
        $windows = $session->window_handles();
0 ignored issues
show
Bug introduced by
The method window_handles() does not exist on WebinoDev\Test\Selenium\WebDriver\SessionInterface. Did you maybe mean window()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
474
        $session->focusWindow($windows[$index]);
0 ignored issues
show
Bug introduced by
The method focusWindow() does not exist on WebinoDev\Test\Selenium\WebDriver\SessionInterface. Did you maybe mean window()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
475
        return $this;
476
    }
477
478
    /**
479
     * Close a browser window
480
     *
481
     * @return $this
482
     */
483
    protected function closeWindow()
484
    {
485
        $this->getSession()->deleteWindow();
0 ignored issues
show
Bug introduced by
The method deleteWindow() does not exist on WebinoDev\Test\Selenium\WebDriver\SessionInterface. Did you maybe mean window()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
486
        $this->focusWindow(0);
487
        return $this;
488
    }
489
490
    /**
491
     * Wait for something, then do something else
492
     *
493
     * @param callable|object $action
494
     * @param callable|object $callback
495
     * @return $this
496
     */
497
    protected function waitFor(callable $action, callable $callback = null)
498
    {
499
        $elm = (new Wait($this->session))->until($action);
500
        $callback and call_user_func($callback, $elm);
501
        return $this;
502
    }
503
504
    /**
505
     * Ajax wait
506
     *
507
     * Depends on jQuery.
508
     *
509
     * @param float $delay Seconds
510
     * @return $this
511
     */
512
    protected function waitForAjax($delay = .1)
513
    {
514
        // delay slightly more than required
515
        $delay and usleep($delay * 1100000);
516
517
        $this->waitFor(function () {
518
            return $this->session->execute(['script' => 'return !jQuery.active', 'args' => []]);
519
        });
520
521
        return $this;
522
    }
523
}
524