Completed
Push — master ( 4a3615...9e5cd8 )
by Kevin
02:59
created

AbstractTestCase::setTestCaseConfigurationClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Magium;
4
5
use Facebook\WebDriver\Exception\NoSuchElementException;
6
use Facebook\WebDriver\Exception\WebDriverException;
7
use Magium\Assertions\Element\Clickable;
8
use Magium\Assertions\Element\Exists;
9
use Magium\Assertions\Element\NotClickable;
10
use Magium\Assertions\Element\NotDisplayed;
11
use Magium\Assertions\Element\NotExists;
12
use Magium\Assertions\LoggingAssertionExecutor;
13
use Magium\TestCase\Initializer;
14
use Magium\Themes\BaseThemeInterface;
15
use Magium\Util\Log\Logger;
16
use Magium\Util\Phpunit\MasterListener;
17
use Magium\WebDriver\WebDriver;
18
use PHPUnit_Framework_TestResult;
19
use Zend\Di\Di;
20
21
abstract class AbstractTestCase extends \PHPUnit_Framework_TestCase
22
{
23
24
    protected static $baseNamespaces = [];
25
26
    protected $baseThemeClass = 'Magium\Themes\ThemeConfigurationInterface';
27
28
    protected $postCallbacks = [];
29
30
    /**
31
     * @var MasterListener
32
     */
33
34
    protected static $masterListener;
35
36
    /**
37
     * @var \Magium\WebDriver\WebDriver
38
     */
39
    protected $webdriver;
40
41
    /**
42
     * @var Di
43
     */
44
45
    protected $di;
46
47
    protected $textElementNodeSearch = [
48
        'button', 'span', 'a', 'li', 'label', 'option', 'h1', 'h2', 'h3', 'td'
49
    ];
50
51
    protected $initializer;
52
53
    const BY_XPATH = 'byXpath';
54
    const BY_ID    = 'byId';
55
    const BY_CSS_SELECTOR = 'byCssSelector';
56
    const BY_TEXT = 'byText';
57
58
    protected static $registrationCallbacks;
59
60
    protected function setUp()
61
    {
62
        /*
63
         * Putting this in the setup and not in the property means that an extending class can inject itself easily
64
         * before the Magium namespace, thus, taking preference over the base namespace
65
         */
66
        self::addBaseNamespace('Magium');
67
        $init = new Initializer();
68
        $init->initialize($this);
69
    }
70
71
    /**
72
     * @return Di
73
     */
74
    public function getDi()
75
    {
76
        return $this->di;
77
    }
78
79
    /**
80
     * @param Di $di
81
     */
82
    public function setDi($di)
83
    {
84
        $this->di = $di;
85
    }
86
87
    /**
88
     * @return WebDriver
89
     */
90
    public function getWebdriver()
91
    {
92
        return $this->webdriver;
93
    }
94
95
    /**
96
     * @param WebDriver $webdriver
97
     */
98
    public function setWebdriver(WebDriver $webdriver)
99
    {
100
        $this->webdriver = $webdriver;
101
    }
102
103
    public function getInitializer()
104
    {
105
        return $this->initializer;
106
    }
107
108
109
    public function __construct($name = null, array $data = [], $dataName = null, Initializer $initializer = null)
110
    {
111
        if (!$initializer instanceof Initializer) {
112
            $initializer = new Initializer();
113
        }
114
        $this->initializer = $initializer;
115
        self::getMasterListener();
116
        parent::__construct($name, $data, $dataName);
117
118
    }
119
120
    public function setTestResultObject(PHPUnit_Framework_TestResult $result)
121
    {
122
        // This odd little function is here because the first place where you can reliably add a listener without
123
        // having to make a phpunit.xml or program argument change
124
125
        self::getMasterListener()->bindToResult($result);
126
        parent::setTestResultObject($result);
127
    }
128
129
    /**
130
     * @return MasterListener
131
     */
132
133
    public static function getMasterListener()
134
    {
135
        if (!self::$masterListener instanceof MasterListener) {
136
            self::$masterListener = new MasterListener();
137
        }
138
        return self::$masterListener;
139
    }
140
141
    protected function tearDown()
142
    {
143
        foreach ($this->postCallbacks as $callback) {
144
            if (is_callable($callback)) {
145
                call_user_func($callback);
146
            }
147
        }
148
        parent::tearDown();
149
        if ($this->webdriver instanceof WebDriver) {
150
            $this->webdriver->quit();
151
            $this->webdriver = null;
152
        }
153
    }
154
155
156
    public function filterWebDriverAction($by)
157
    {
158
        switch ($by) {
159
            case WebDriver::BY_XPATH:
160
                return 'xpath';
161
            case WebDriver::BY_CSS_SELECTOR:
162
                return 'css_selector';
163
            case WebDriver::BY_ID:
164
                return 'id';
165
            default:
166
                return $by;
167
        }
168
    }
169
170
    public function assertElementClickable($selector, $by = WebDriver::BY_ID)
171
    {
172
        $this->elementAssertion($selector, $by, Clickable::ASSERTION);
173
    }
174
175
176
    public function assertElementNotClickable($selector, $by = WebDriver::BY_ID)
177
    {
178
        $this->elementAssertion($selector, $by, NotClickable::ASSERTION);
179
180
    }
181
182
    public static function addBaseNamespace($namespace)
183
    {
184
        if (!in_array($namespace, self::$baseNamespaces)) {
185
            self::$baseNamespaces[] = trim($namespace, '\\');
186
        }
187
    }
188
189
    public static function resolveClass( $class, $prefix = null)
190
    {
191
        $origClass = $class;
192
        if ($prefix !== null) {
193
            $class = "{$prefix}\\{$class}";
194
        }
195
        foreach (self::$baseNamespaces as $namespace) {
196
            if (strpos($namespace, $class) === 0) {
197
                // We have a fully qualified class name
198
                return $class;
199
            }
200
        }
201
202
        foreach (self::$baseNamespaces as $namespace) {
203
            $fqClass = $namespace . '\\' . $class;
204
            if (class_exists($fqClass)) {
205
                return $fqClass;
206
            }
207
        }
208
        return $origClass;
209
    }
210
211
    public function setTypePreference($type, $preference)
212
    {
213
        $type = self::resolveClass($type);
214
        $preference = self::resolveClass($preference);
215
        $this->di->instanceManager()->unsetTypePreferences($type);
216
        $this->di->instanceManager()->setTypePreference($type, [$preference]);
217
218
    }
219
220
    protected function normalizeClassRequest($class)
221
    {
222
        return str_replace('/', '\\', $class);
223
    }
224
225
    public function addPostTestCallback($callback)
226
    {
227
        if (!is_callable($callback)) {
228
            throw new InvalidConfigurationException('Callback is not callable');
229
        }
230
        $this->postCallbacks[] = $callback;
231
    }
232
233
    /**
234
235
     * @param string $theme
236
     * @return \Magium\Themes\ThemeConfigurationInterface
237
     */
238
239
    public function getTheme($theme = null)
240
    {
241
        if ($theme === null) {
242
            return $this->get($this->baseThemeClass);
243
        }
244
        $theme = self::resolveClass($theme, 'Themes');
245
        return $this->get($theme);
246
    }
247
248
    /**
249
     *
250
     * @param string $action
251
     * @return mixed
252
     */
253
254
    public function getAction($action)
255
    {
256
        $action = self::resolveClass($action, 'Actions');
257
258
        return $this->get($action);
259
    }
260
261
262
    /**
263
     * @param string $name
264
     * @return \Magium\Magento\Identities\AbstractEntity
265
     */
266
267
    public function getIdentity($name = 'Customer')
268
    {
269
        $name = self::resolveClass($name, 'Identities');
270
271
        return $this->get($name);
272
    }
273
274
    /**
275
     *
276
     * @param string $navigator
277
     * @return \Magium\Magento\Navigators\BaseMenuNavigator
278
     */
279
280
    public function getNavigator($navigator = 'BaseMenu')
281
    {
282
        $navigator = self::resolveClass($navigator, 'Navigators');
283
284
        return $this->get($navigator);
285
    }
286
287
    public function getAssertion($assertion)
288
    {
289
        $assertion = self::resolveClass($assertion, 'Assertions');
290
291
        return $this->get($assertion);
292
    }
293
294
    /**
295
     *
296
     * @param string $extractor
297
     * @return \Magium\Extractors\AbstractExtractor
298
     */
299
300
    public function getExtractor($extractor)
301
    {
302
        $extractor = self::resolveClass($extractor, 'Extractors');
303
304
        return $this->get($extractor);
305
    }
306
307
    /**
308
     * Sleep the specified amount of time.
309
     *
310
     * Options: 1s (1 second), 1ms (1 millisecond), 1us (1 microsecond), 1ns (1 nanosecond)
311
     *
312
     * @param $time
313
     */
314
315
    public function sleep($time)
316
    {
317
        $length = (int)$time;
318
319
        if (strpos($time, 'ms') !== false) {
320
            usleep($length * 1000);
321
        } else if (strpos($time, 'us') !== false) {
322
            usleep($length);
323
        } else if (strpos($time, 'ns') !== false) {
324
            time_nanosleep(0, $length);
325
        } else {
326
            sleep($length);
327
        }
328
    }
329
330
331
    public function commandOpen($url)
332
    {
333
        $this->get('Magium\Commands\Open')->open($url);
334
    }
335
336
337
    /**
338
     * @return \Magium\Util\Log\Logger
339
     */
340
341
    public function getLogger()
342
    {
343
        return $this->get('Magium\Util\Log\Logger');
344
    }
345
346
    public function get($class)
347
    {
348
        $class = $this->normalizeClassRequest($class);
349
        $preferredClass = $this->di->instanceManager()->getTypePreferences($class);
350
        if (is_array($preferredClass) && count($preferredClass) > 0) {
351
            $class = array_shift($preferredClass);
352
        }
353
        return $this->di->get($class);
354
    }
355
356
357
    public function assertElementExists($selector, $by = 'byId')
358
    {
359
        $this->elementAssertion($selector, $by, Exists::ASSERTION);
360
    }
361
362
    public function assertTitleEquals($title)
363
    {
364
        $webTitle = $this->webdriver->getTitle();
365
        self::assertEquals($title, $webTitle);
366
    }
367
368
369
    public function assertTitleContains($title)
370
    {
371
        $webTitle = $this->webdriver->getTitle();
372
        self::assertContains($title, $webTitle);
373
    }
374
375
376
    public function assertNotTitleEquals($title)
377
    {
378
        $webTitle = $this->webdriver->getTitle();
379
        self::assertNotEquals($title, $webTitle);
380
    }
381
382
383
    public function assertNotTitleContains($title)
384
    {
385
        $webTitle = $this->webdriver->getTitle();
386
        self::assertNotContains($title, $webTitle);
387
    }
388
389
    public function assertURLEquals($url)
390
    {
391
        $webUrl = $this->webdriver->getCurrentURL();
392
        self::assertEquals($url, $webUrl);
393
    }
394
395
    public function assertURLContains($url)
396
    {
397
        $webUrl = $this->webdriver->getCurrentURL();
398
        self::assertContains($url, $webUrl);
399
    }
400
401
402
    public function assertURLNotEquals($url)
403
    {
404
        $webUrl = $this->webdriver->getCurrentURL();
405
        self::assertNotEquals($url, $webUrl);
406
    }
407
408
    public function assertURLNotContains($url)
409
    {
410
        $webUrl = $this->webdriver->getCurrentURL();
411
        self::assertNotContains($url, $webUrl);
412
    }
413
414
    protected function elementAssertion($selector, $by, $name)
415
    {
416
        $executor = $this->getAssertion(LoggingAssertionExecutor::ASSERTION);
417
        $assertion = $this->getAssertion($name);
418
        $assertion->setSelector($selector)->setBy($by);
419
        $executor->execute($assertion);
420
    }
421
422
    public function assertElementDisplayed($selector, $by = 'byId')
423
    {
424
        $this->elementAssertion($selector, $by, NotDisplayed::ASSERTION);
425
    }
426
427
    public function assertElementNotDisplayed($selector, $by = 'byId')
428
    {
429
        try {
430
            $this->assertElementExists($selector, $by);
431
            self::assertFalse(
432
                $this->webdriver->$by($selector)->isDisplayed(),
433
                sprintf('The element: %s, is displayed and it should not have been', $selector)
434
            );
435
        } catch (\Exception $e) {
436
            $this->fail(sprintf('Element "%s" cannot be found using selector "%s"', $selector, $by));
437
        }
438
    }
439
440
    public function assertElementNotExists($selector, $by = 'byId')
441
    {
442
        $this->elementAssertion($selector, $by, NotExists::ASSERTION);
443
    }
444
445
    /**
446
     * @return LoggingAssertionExecutor
447
     */
448
449
    public function getAssertionLogger()
450
    {
451
        return $this->getAssertion(LoggingAssertionExecutor::ASSERTION);
452
    }
453
454
    public function switchThemeConfiguration($fullyQualifiedClassName)
455
    {
456
457
        $reflection = new \ReflectionClass($fullyQualifiedClassName);
458
459
        if ($reflection->implementsInterface('Magium\Themes\ThemeConfigurationInterface')) {
460
            $this->baseThemeClass = $fullyQualifiedClassName;
461
            $this->di->instanceManager()->unsetTypePreferences('Magium\Themes\ThemeConfigurationInterface');
462
            $this->di->instanceManager()->setTypePreference('Magium\Themes\ThemeConfigurationInterface', [$fullyQualifiedClassName]);
463
464
            if ($reflection->implementsInterface('Magium\Themes\BaseThemeInterface')) {
465
                $this->di->instanceManager()->unsetTypePreferences('Magium\Themes\BaseThemeInterface');
466
                $this->di->instanceManager()->setTypePreference('Magium\Themes\BaseThemeInterface', [$fullyQualifiedClassName]);
467
            }
468
            $theme = $this->getTheme();
469
            if ($theme instanceof BaseThemeInterface) {
470
                $theme->configure($this);
471
            }
472
        } else {
473
            throw new InvalidConfigurationException('The theme configuration implement Magium\Themes\ThemeConfigurationInterface');
474
        }
475
        $this->getLogger()->addCharacteristic(Logger::CHARACTERISTIC_THEME, $fullyQualifiedClassName);
476
    }
477
478
    public static function assertWebDriverElement($element)
479
    {
480
        self::assertInstanceOf('Facebook\WebDriver\WebDriverElement', $element);
481
    }
482
483
    public function assertElementHasText($node, $text)
484
    {
485
        try {
486
            $this->byXpath(sprintf('//%s[contains(., "%s")]', $node, addslashes($text)));
487
        } catch (\Exception $e) {
488
            $this->fail('The body did not contain the text: ' . $text);
489
        }
490
    }
491
492
    public function assertPageHasText($text)
493
    {
494
        try {
495
            $this->webdriver->byXpath(sprintf('//body[contains(., "%s")]', $text));
496
            // If the element is not found then an exception will be thrown
497
        } catch (\Exception $e) {
498
            $this->fail('The body did not contain the text: ' . $text);
499
        }
500
501
    }
502
503
    public function assertPageNotHasText($text)
504
    {
505
        try {
506
            $this->webdriver->byXpath(sprintf('//body[contains(., "%s")]', $text));
507
            $this->fail('The page contains the words: ' . $text);
508
        } catch (NoSuchElementException $e) {
509
            // Exception thrown is a success
510
        }
511
    }
512
513
    /**
514
     * @param $xpath
515
     * @return \Facebook\WebDriver\Remote\RemoteWebElement
516
     */
517
518
    public function byXpath($xpath)
519
    {
520
        return $this->webdriver->byXpath($xpath);
521
    }
522
523
    /**
524
     * @param $id
525
     * @return \Facebook\WebDriver\Remote\RemoteWebElement
526
     */
527
528
    public function byId($id)
529
    {
530
        return $this->webdriver->byId($id);
531
    }
532
533
    /**
534
     * @param $selector
535
     * @return \Facebook\WebDriver\Remote\RemoteWebElement
536
     */
537
538
    public function byCssSelector($selector)
539
    {
540
        return $this->webdriver->byCssSelector($selector);
541
    }
542
543
    protected function getElementByTextXpath($xpathTemplate, $text, $specificNodeType = null, $parentElementSelector = null)
544
    {
545
546
        if ($parentElementSelector !== null) {
547
            $xpathTemplate = $parentElementSelector . $xpathTemplate;
548
        }
549
        if ($specificNodeType !== null) {
550
            return $this->byXpath(sprintf($xpathTemplate, $specificNodeType, $this->getTranslator()->translatePlaceholders($text)));
551
        }
552
553
        foreach ($this->textElementNodeSearch as $nodeName) {
554
            $xpath = sprintf($xpathTemplate, $nodeName, $this->getTranslator()->translatePlaceholders($text));
555
            if ($this->webdriver->elementExists($xpath, WebDriver::BY_XPATH)) {
556
                return $this->webdriver->byXpath($xpath);
557
            }
558
        }
559
        // This is here for consistency with the other by* methods
560
        WebDriverException::throwException(7, 'Could not find element with text: ' . $this->getTranslator()->translatePlaceholders($text), []);
561
    }
562
563
    /**
564
     * @param string $text
565
     * @param string $specificNodeType
566
     * @param string $parentElementSelector
567
     * @return \Facebook\WebDriver\Remote\RemoteWebElement
568
     */
569
    public function byText($text, $specificNodeType = null, $parentElementSelector = null)
570
    {
571
        $xpathTemplate = '//%s[concat(" ",normalize-space(.)," ") = " %s "]';
572
        return $this->getElementByTextXpath($xpathTemplate, $text, $specificNodeType, $parentElementSelector);
573
    }
574
575
576
    /**
577
     * @param string $text
578
     * @param string $specificNodeType
579
     * @param string $parentElementSelector
580
     * @return \Facebook\WebDriver\Remote\RemoteWebElement
581
     */
582
    public function byContainsText($text, $specificNodeType = null, $parentElementSelector = null)
583
    {
584
        $xpathTemplate = '//%s[contains(., "%s")]';
585
        return $this->getElementByTextXpath($xpathTemplate, $text, $specificNodeType, $parentElementSelector);
586
    }
587
588
    /**
589
     * @return \Magium\Util\Translator\Translator
590
     */
591
592
    public function getTranslator()
593
    {
594
        return $this->get('Magium\Util\Translator\Translator');
595
    }
596
597
    public function addTranslationCsvFile($file, $locale)
598
    {
599
        $this->getTranslator()->addTranslationCsvFile($file, $locale);
600
    }
601
}
602