Completed
Push — develop ( 4c09ba...a749c9 )
by Peter
08:03
created

AbstractTestCase::enterInput()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 25
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 17
nc 4
nop 3
dl 0
loc 25
rs 8.439
c 0
b 0
f 0
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;
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 $browser;
51
52
    /**
53
     * @var string
54
     */
55
    protected $browserBin;
56
57
    /**
58
     * @var string
59
     */
60
    protected $uri;
61
62
    /**
63
     * @var PHPWebDriver_WebDriver
64
     */
65
    protected $webDriver;
66
67
    /**
68
     * @var \PHPWebDriver_WebDriverSession|WebDriver\SessionInterface
69
     */
70
    protected $session;
71
72
    /**
73
     * Resolves URI to open session
74
     */
75
    protected function setUp()
76
    {
77
        $this->uri       = $this->resolveUri();
78
        $this->webDriver = new PHPWebDriver_WebDriver($this->resolveHost());
79
        $this->session   = $this->webDriver->session($this->resolveBrowser(), $this->resolveCapabilities());
80
81
        // TODO more window sizes
82
        //$this->session->window()->postSize(['width' => 1280, 'height' => 720]);
83
//        $this->session->window()->postSize(['width' => 1920, 'height' => 1080]);
84
    }
85
86
    /**
87
     * @return void
88
     */
89
    protected function tearDown()
90
    {
91
        $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...
92
    }
93
94
    /**
95
     * @param Exception $exc
96
     * @throws Exception
97
     */
98
    protected function onNotSuccessfulTest(Exception $exc)
99
    {
100
        $this->notifyError($exc);
101
//        $this->session->close();
102
        parent::onNotSuccessfulTest($exc);
103
    }
104
105
    /**
106
     * @return string
107
     */
108
    protected function getBrowser()
109
    {
110
        return $this->browser ? $this->browser : $this::$webDriverBrowser;
111
    }
112
113
    /**
114
     * @return string
115
     */
116
    protected function getBrowserBin()
117
    {
118
        return $this->browserBin;
119
    }
120
121
    /**
122
     * @param string $bin
123
     * @return string
124
     */
125
    protected function setBrowserBin($bin)
126
    {
127
        $this->browserBin = (string) $bin;
128
        return $this;
129
    }
130
131
    /**
132
     * @return \PHPWebDriver_WebDriverSession|WebDriver\SessionInterface
133
     */
134
    public function getSession()
135
    {
136
        return $this->session;
137
    }
138
139
    /**
140
     * @return string
141
     */
142
    protected function getSessionId()
143
    {
144
        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...
145
    }
146
147
    /**
148
     * @return string
149
     */
150
    protected function getServerUrl()
151
    {
152
        $parts = parse_url($this->session->url());
153
        return $parts['scheme'] . '://' . $parts['host'];
154
    }
155
156
    /**
157
     * Get raw source from URL
158
     *
159
     * @param string $url
160
     * @param null $sessId
161
     * @return string
162
     */
163
    protected function source($url, $sessId = null)
164
    {
165
        $sid = $sessId ? $sessId : $this->getSessionId();
166
        $opts = ['http' => ['header' => 'Cookie: PHPSESSID=' . $sid ."\r\n"]];
167
        return file_get_contents($url, false, stream_context_create($opts));
168
    }
169
170
    /**
171
     * Resolve Selenium WebDriver host
172
     *
173
     * @return string
174
     */
175
    protected function resolveHost()
176
    {
177
        $host = getenv('HOST');
178
        empty($host) and $host = $this::$webDriverHost;
179
        return sprintf($host, $this->resolvePort());
180
    }
181
182
    /**
183
     * Resolve Selenium WebDriver port
184
     *
185
     * @return string
186
     */
187
    protected function resolvePort()
188
    {
189
        $port = getenv('PORT');
190
        empty($port) and $port = $this::$webDriverPort;
191
        return $port;
192
    }
193
194
    /**
195
     * Resolve test session browser
196
     *
197
     * @return string
198
     */
199
    protected function resolveBrowser()
200
    {
201
        $browser = getenv('BROWSER');
202
        empty($browser) and $browser = $this::$webDriverBrowser;
203
204
        switch ($browser) {
205
            case 'chromium':
206
                $browser = 'chrome';
207
                $this->setBrowserBin('/usr/bin/chromium-browser');
208
                break;
209
            case 'firefox':
210
                $this->setBrowserBin('/var/lib/webino/firefox/firefox');
211
                break;
212
        }
213
214
        return $this->browser = $browser;
215
    }
216
217
    /**
218
     * Resolve test session capabilities
219
     *
220
     * @return array
221
     */
222
    protected function resolveCapabilities()
223
    {
224
        switch ($this->getBrowser()) {
225
            case 'chrome':
226
                // Fixes OpenVZ
227
                $opts = ['args' => ['no-sandbox', 'start-maximized']];
228
                $bin  = $this->getBrowserBin();
229
                $bin and $opts+= ['binary' => $bin];
230
                return ['chromeOptions' => $opts];
231
            case 'firefox':
232
                $opts = [];
233
                $bin  = $this->getBrowserBin();
234
                $bin and $opts+= ['firefox_binary' => $bin];
235
                return $opts;
236
        }
237
        return [];
238
    }
239
240
    /**
241
     * Resolve test target URI
242
     *
243
     * @return string
244
     */
245
    protected function resolveUri()
246
    {
247
        $uri = getenv('URI');
248
        if (empty($uri)) {
249
            throw new RuntimeException('Expected URI env');
250
        }
251
        return $uri;
252
    }
253
254
    /**
255
     * @param float $sec
256
     * @return $this
257
     */
258
    protected function sleep($sec = 2.0)
259
    {
260
        sleep($sec);
261
        return $this;
262
    }
263
264
    /**
265
     * Opens URI and asserts not error
266
     *
267
     * @param string $path
268
     * @param string $caption
269
     * @return $this
270
     */
271
    protected function openOk($path = '', $caption = 'Home')
272
    {
273
        $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...
274
        $this->debugNotify($caption);
275
        $this->attachScreenshot($caption);
276
        $this->assertNotError();
277
        return $this;
278
    }
279
280
    /**
281
     * Assert that page is without errors
282
     *
283
     * @return $this
284
     */
285
    protected function assertNotError()
286
    {
287
        $this->assertNotContains('Error', $this->session->title());
288
289
        // strip script contents & tags
290
        $text = $this->session->source();
291
        $expr = '~<(script).*?>.*?</script>~si';
292
        $src  = strip_tags(preg_replace($expr, '', $text));
293
294
        $this->assertNotContains('Error', $src);
295
        $this->assertNotContains('Exception', $src);
296
        return $this;
297
    }
298
299
    /**
300
     * @return bool
301
     */
302
    public function is404()
303
    {
304
        $url = $this->session->url();
305
        $headers = get_headers($url);
306
        if (empty($headers[0])) {
307
            return true;
308
        }
309
310
        return 'HTTP/1.0 404 Not Found' === $headers[0];
311
    }
312
313
    /**
314
     * Clicks on a link
315
     *
316
     * @param string $linkText
317
     * @param callable $callback
318
     * @return $this
319
     */
320
    protected function clickLink($linkText, callable $callback = null)
321
    {
322
        $elm = $this->elementByLinkText($linkText);
323
        $elm->click();
324
        $this->attachScreenshot('Click ' . $linkText);
325
        $callback and call_user_func($callback, $elm);
326
        return $this;
327
    }
328
329
    /**
330
     * Clicks on ajax-link
331
     *
332
     * @param string $linkText
333
     * @param callable $callback
334
     * @return $this
335
     */
336
    protected function clickAjaxLink($linkText, callable $callback = null)
337
    {
338
        $this->clickLink($linkText, $callback);
339
        $this->attachScreenshot('Click ' . $linkText);
340
        $this->waitForAjax();
341
        $this->attachScreenshot($linkText);
342
        return $this;
343
    }
344
345
    /**
346
     * @param string $class
347
     * @return $this
348
     */
349
    protected function clickByClass($class)
350
    {
351
        $this->elementByClassName($class)->click();
352
        return $this;
353
    }
354
355
    /**
356
     * @param string $name
357
     * @param string $value
358
     * @return $this
359
     * @deprecated use getSelect()
360
     */
361
    protected function clickSelect($name, $value)
362
    {
363
        $selector = sprintf('select[name="%s"] option[value="%s"]', $name, $value);
364
        $this->elementByCssSelector($selector)->click();
365
        return $this;
366
    }
367
368
    /**
369
     * @param string $name
370
     * @return WebDriver\Select
371
     */
372
    protected function getSelect($name)
373
    {
374
        return new WebDriver\Select($this->elementByName($name));
375
    }
376
377
    /**
378
     * Enters the input value
379
     *
380
     * @param string|PHPWebDriver_WebDriverElement $name
381
     * @param string $value
382
     * @param callable|false|null $callback
383
     * @return $this
384
     */
385
    protected function enterInput($name, $value, $callback = null)
386
    {
387
        $resolveElm = function () use ($name) {
388
            if ($name instanceof WebDriver\ElementInterface
389
                || $name instanceof PHPWebDriver_WebDriverElement
0 ignored issues
show
Bug introduced by
The class PHPWebDriver_WebDriverElement does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
390
            ) {
391
                return $name;
392
            }
393
            return $this->elementByName($name);
394
        };
395
396
        /** @var PHPWebDriver_WebDriverElement $elm */
397
        $elm = $resolveElm();
398
        if (null === $callback) {
399
            $this->sleep(1);
400
            $elm->clear();
401
            $elm->sendKeys('');
402
            $this->sleep(1);
403
        }
404
        $elm->clear();
405
        $elm->sendKeys($value);
406
        $this->attachScreenshot('Input ' . $name);
407
        is_callable($callback) and call_user_func($callback, $elm);
408
        return $this;
409
    }
410
411
    /**
412
     * Submit the input value
413
     *
414
     * @param string|PHPWebDriver_WebDriverElement $name
415
     * @param string $value
416
     * @return $this
417
     */
418
    protected function submitInput($name, $value)
419
    {
420
        $this->enterInput($name, $value, function ($elm) {
421
            $elm->submit();
422
        });
423
        return $this;
424
    }
425
426
    /**
427
     * Assert that input value is same than expected
428
     *
429
     * @param string|PHPWebDriver_WebDriverElement $name
430
     * @param string $expectedValue
431
     * @param callable $callback
432
     * @return $this
433
     */
434
    public function assertInput($name, $expectedValue, callable $callback = null)
435
    {
436
        $elm = ($name instanceof PHPWebDriver_WebDriverElement) ? $name : $this->elementByName($name);
437
        $this->assertSame($expectedValue, $elm->attribute('value'));
438
        $callback and call_user_func($callback, $elm);
439
        return $this;
440
    }
441
442
    /**
443
     * Focus a browser window
444
     *
445
     * @param int $index
446
     * @return $this
447
     */
448
    protected function focusWindow($index)
449
    {
450
        $session = $this->getSession();
451
        $windows = $session->window_handles();
0 ignored issues
show
Bug introduced by
The method window_handles() 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...
452
        $session->focusWindow($windows[$index]);
0 ignored issues
show
Bug introduced by
The method focusWindow() 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...
453
        return $this;
454
    }
455
456
    /**
457
     * Close a browser window
458
     *
459
     * @return $this
460
     */
461
    protected function closeWindow()
462
    {
463
        $this->getSession()->deleteWindow();
0 ignored issues
show
Bug introduced by
The method deleteWindow() 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...
464
        $this->focusWindow(0);
465
        return $this;
466
    }
467
468
    /**
469
     * Wait for something, then do something else
470
     *
471
     * @param callable $action
472
     * @param callable $callback
473
     * @return $this
474
     */
475
    protected function waitFor(callable $action, callable $callback = null)
476
    {
477
        $elm = (new Wait($this->session))->until($action);
478
        $callback and call_user_func($callback, $elm);
479
        return $this;
480
    }
481
482
    /**
483
     * Ajax wait
484
     *
485
     * Depends on jQuery.
486
     *
487
     * @param float $delay Seconds
488
     * @return $this
489
     */
490
    protected function waitForAjax($delay = .1)
491
    {
492
        // delay slightly more than required
493
        $delay and usleep($delay * 1100000);
494
495
        $this->waitFor(function () {
496
            return $this->session->execute(['script' => 'return !jQuery.active', 'args' => []]);
497
        });
498
499
        return $this;
500
    }
501
}
502