Completed
Push — develop ( 55c5fe...0f7fe1 )
by Peter
09:30
created

AbstractTestCase::waitFor()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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