Completed
Push — develop ( a7a448...fe1f55 )
by Peter
01:42
created

AbstractTestCase::resolveCapabilities()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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