Completed
Push — master ( 9ad6b1...7b0007 )
by Kevin
05:22 queued 02:37
created

AbstractTestCase::assertElementDisplayed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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