Completed
Push — develop ( 0f7fe1...c45449 )
by Peter
10:05
created

AbstractTestCase   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 462
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 0
loc 462
rs 8.5454
c 0
b 0
f 0
wmc 49
lcom 1
cbo 5

31 Methods

Rating   Name   Duplication   Size   Complexity  
A setUp() 0 10 1
A tearDown() 0 4 2
A onNotSuccessfulTest() 0 6 1
A getBrowser() 0 4 1
A getBrowserBin() 0 4 1
A setBrowserBin() 0 5 1
A getSession() 0 4 1
A getSessionId() 0 4 1
A getServerUrl() 0 5 1
A source() 0 6 2
A resolveHost() 0 6 2
A resolvePort() 0 6 2
A resolveBrowser() 0 14 3
A resolveCapabilities() 0 12 3
A resolveUri() 0 8 2
A sleep() 0 5 1
A openOk() 0 8 1
A assertNotError() 0 13 1
A is404() 0 10 2
A clickLink() 0 8 2
A clickAjaxLink() 0 8 1
A clickByClass() 0 5 1
A clickSelect() 0 6 1
A getSelect() 0 4 1
A enterInput() 0 20 4
A submitInput() 0 7 1
A assertInput() 0 7 3
A focusWindow() 0 7 1
A closeWindow() 0 6 1
A waitFor() 0 6 2
A waitForAjax() 0 11 2

How to fix   Complexity   

Complex Class

Complex classes like AbstractTestCase often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractTestCase, and based on these observations, apply Extract Interface, too.

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 = 'htmlunit';
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
        }
205
206
        return $this::$browser;
207
    }
208
209
    /**
210
     * Resolve test session capabilities
211
     *
212
     * @return array
213
     */
214
    protected function resolveCapabilities()
215
    {
216
        switch ($this->getBrowser()) {
217
            case 'chrome':
218
                // Fixes OpenVZ
219
                $opts = ['args' => ['no-sandbox', 'start-maximized']];
220
                $bin  = $this->getBrowserBin();
221
                $bin and $opts+= ['binary' => $bin];
222
                return ['chromeOptions' => $opts];
223
        }
224
        return [];
225
    }
226
227
    /**
228
     * Resolve test target URI
229
     *
230
     * @return string
231
     */
232
    protected function resolveUri()
233
    {
234
        $uri = getenv('URI');
235
        if (empty($uri)) {
236
            throw new RuntimeException('Expected URI env');
237
        }
238
        return $uri;
239
    }
240
241
    /**
242
     * @param float $sec
243
     * @return $this
244
     */
245
    protected function sleep($sec)
246
    {
247
        sleep($sec);
248
        return $this;
249
    }
250
251
    /**
252
     * Opens URI and asserts not error
253
     *
254
     * @param string $path
255
     * @param string $caption
256
     * @return $this
257
     */
258
    protected function openOk($path = '', $caption = 'Home')
259
    {
260
        $this->session->open($this->uri . $path);
261
        $this->debugNotify($caption);
262
        $this->attachScreenshot($caption);
263
        $this->assertNotError();
264
        return $this;
265
    }
266
267
    /**
268
     * Assert that page is without errors
269
     *
270
     * @return $this
271
     */
272
    protected function assertNotError()
273
    {
274
        $this->assertNotContains('Error', $this->session->title());
275
276
        // strip script contents & tags
277
        $text = $this->session->source();
278
        $expr = '~<(script).*?>.*?</script>~si';
279
        $src  = strip_tags(preg_replace($expr, '', $text));
280
281
        $this->assertNotContains('Error', $src);
282
        $this->assertNotContains('Exception', $src);
283
        return $this;
284
    }
285
286
    /**
287
     * @return bool
288
     */
289
    public function is404()
290
    {
291
        $url = $this->session->url();
292
        $headers = get_headers($url);
293
        if (empty($headers[0])) {
294
            return true;
295
        }
296
297
        return 'HTTP/1.0 404 Not Found' === $headers[0];
298
    }
299
300
    /**
301
     * Clicks on a link
302
     *
303
     * @param string $linkText
304
     * @param callable $callback
305
     * @return $this
306
     */
307
    protected function clickLink($linkText, callable $callback = null)
308
    {
309
        $elm = $this->elementByLinkText($linkText);
310
        $elm->click();
311
        $this->attachScreenshot('Click ' . $linkText);
312
        $callback and call_user_func($callback, $elm);
313
        return $this;
314
    }
315
316
    /**
317
     * Clicks on ajax-link
318
     *
319
     * @param string $linkText
320
     * @param callable $callback
321
     * @return $this
322
     */
323
    protected function clickAjaxLink($linkText, callable $callback = null)
324
    {
325
        $this->clickLink($linkText, $callback);
326
        $this->attachScreenshot('Click ' . $linkText);
327
        $this->waitForAjax();
328
        $this->attachScreenshot($linkText);
329
        return $this;
330
    }
331
332
    /**
333
     * @param string $class
334
     * @return $this
335
     */
336
    protected function clickByClass($class)
337
    {
338
        $this->elementByClassName($class)->click();
339
        return $this;
340
    }
341
342
    /**
343
     * @param string $name
344
     * @param string $value
345
     * @return $this
346
     * @deprecated use getSelect()
347
     */
348
    protected function clickSelect($name, $value)
349
    {
350
        $selector = sprintf('select[name="%s"] option[value="%s"]', $name, $value);
351
        $this->elementByCssSelector($selector)->click();
352
        return $this;
353
    }
354
355
    /**
356
     * @return WebDriver\Select
357
     */
358
    protected function getSelect($name)
359
    {
360
        return new WebDriver\Select($this->elementByName($name));
361
    }
362
363
    /**
364
     * Enters the input value
365
     *
366
     * @param string|PHPWebDriver_WebDriverElement $name
367
     * @param string $value
368
     * @param callable|false|null $callback
369
     * @return $this
370
     */
371
    protected function enterInput($name, $value, $callback = null)
372
    {
373
        $resolveElm = function () use ($name) {
374
            return ($name instanceof PHPWebDriver_WebDriverElement) ? $name : $this->elementByName($name);
375
        };
376
377
        /** @var PHPWebDriver_WebDriverElement $elm */
378
        $elm = $resolveElm();
379
        if (null === $callback) {
380
            sleep(1);
381
            $elm->clear();
382
            $elm->sendKeys('');
383
            sleep(1);
384
        }
385
        $elm->clear();
386
        $elm->sendKeys($value);
387
        $this->attachScreenshot('Input ' . $name);
388
        is_callable($callback) and call_user_func($callback, $elm);
389
        return $this;
390
    }
391
392
    /**
393
     * Submit the input value
394
     *
395
     * @param string|PHPWebDriver_WebDriverElement $name
396
     * @param string $value
397
     * @return $this
398
     */
399
    protected function submitInput($name, $value)
400
    {
401
        $this->enterInput($name, $value, function ($elm) {
402
            $elm->submit();
403
        });
404
        return $this;
405
    }
406
407
    /**
408
     * Assert that input value is same than expected
409
     *
410
     * @param string|PHPWebDriver_WebDriverElement $name
411
     * @param string $expectedValue
412
     * @param callable $callback
413
     * @return $this
414
     */
415
    public function assertInput($name, $expectedValue, callable $callback = null)
416
    {
417
        $elm = ($name instanceof PHPWebDriver_WebDriverElement) ? $name : $this->elementByName($name);
418
        $this->assertSame($expectedValue, $elm->attribute('value'));
419
        $callback and call_user_func($callback, $elm);
420
        return $this;
421
    }
422
423
    /**
424
     * Focus a browser window
425
     *
426
     * @param int $index
427
     * @return $this
428
     */
429
    protected function focusWindow($index)
430
    {
431
        $session = $this->getSession();
432
        $windows = $session->window_handles();
433
        $session->focusWindow($windows[$index]);
434
        return $this;
435
    }
436
437
    /**
438
     * Close a browser window
439
     *
440
     * @return $this
441
     */
442
    protected function closeWindow()
443
    {
444
        $this->getSession()->deleteWindow();
445
        $this->focusWindow(0);
446
        return $this;
447
    }
448
449
    /**
450
     * Wait for something, then do something else
451
     *
452
     * @param callable $action
453
     * @param callable $callback
454
     * @return $this
455
     */
456
    protected function waitFor(callable $action, callable $callback = null)
457
    {
458
        $elm = (new Wait($this->session))->until($action);
459
        $callback and call_user_func($callback, $elm);
460
        return $this;
461
    }
462
463
    /**
464
     * Ajax wait
465
     *
466
     * Depends on jQuery.
467
     *
468
     * @param float $delay Seconds
469
     * @return $this
470
     */
471
    protected function waitForAjax($delay = .1)
472
    {
473
        // delay slightly more than required
474
        $delay and usleep($delay * 1100000);
475
476
        $this->waitFor(function () {
477
            return $this->session->execute(['script' => 'return !jQuery.active', 'args' => []]);
478
        });
479
480
        return $this;
481
    }
482
}
483