Passed
Push — master ( 4ee9ec...849354 )
by San
04:18 queued 03:37
created

BrowserContext   B

Complexity

Total Complexity 54

Size/Duplication

Total Lines 446
Duplicated Lines 14.8 %

Coupling/Cohesion

Components 3
Dependencies 10

Importance

Changes 0
Metric Value
wmc 54
lcom 3
cbo 10
dl 66
loc 446
rs 7.0642
c 0
b 0
f 0

32 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A closeBrowser() 0 4 1
A startTimer() 0 4 1
A iSetBasicAuthenticationWithAnd() 0 4 1
A iAmOnUrlComposedBy() 0 10 2
A iClickOnTheNthElement() 0 5 1
A iFollowTheNthLink() 0 6 1
A pressTheNthButton() 0 6 1
A iFillInWithTheCurrentDate() 0 4 1
A iFillInWithTheCurrentDateAndModifier() 0 5 1
A iHoverIShouldSeeIn() 8 8 2
A iSaveTheValueOfInTheParameter() 0 10 2
A iWaitSecondsUntilISee() 0 4 1
A iDontSeeInSeconds() 0 12 2
A iWaitUntilISee() 0 4 1
B iWaitSecondsUntilISeeInTheElement() 14 28 5
A iWaitSeconds() 0 4 1
A iWaitUntilISeeInTheElement() 0 4 1
A iWaitForElement() 0 4 1
B iWaitSecondsForElement() 12 23 5
A iShouldSeeNElementInTheNthParent() 7 7 2
A iShouldSeeLessThanNElementInTheNthParent() 7 7 2
A iShouldSeeMoreThanNElementInTheNthParent() 7 7 2
A theElementShouldBeEnabled() 11 11 3
A theElementShouldBeDisabled() 0 6 1
A theSelectBoxShouldContain() 0 16 2
A theSelectBoxShouldNotContain() 0 6 1
A theElementShouldBeVisible() 0 11 2
A theElementShouldNotBeVisible() 0 8 1
A switchToIFrame() 0 4 1
A switchToMainFrame() 0 4 1
A elapsedTime() 0 21 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like BrowserContext 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 BrowserContext, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Behatch\Context;
4
5
use Behat\Gherkin\Node\TableNode;
6
use Behat\Mink\Exception\ExpectationException;
7
use Behat\Mink\Exception\ResponseTextException;
8
use Behat\Mink\Exception\ElementNotFoundException;
9
use WebDriver\Exception\StaleElementReference;
10
use Behat\Behat\Tester\Exception\PendingException;
11
12
class BrowserContext extends BaseContext
13
{
14
    private $timeout;
15
    private $dateFormat = 'dmYHi';
16
    private $timerStartedAt;
17
18
    public function __construct($timeout = 1)
19
    {
20
        $this->timeout = $timeout;
21
    }
22
23
    /**
24
     * @AfterScenario
25
     */
26
    public function closeBrowser()
27
    {
28
        $this->getSession()->stop();
29
    }
30
31
    /**
32
     * @BeforeScenario
33
     *
34
     * @When (I )start timing now
35
     */
36
    public function startTimer()
37
    {
38
        $this->timerStartedAt = time();
39
    }
40
41
    /**
42
     * Set login / password for next HTTP authentication
43
     *
44
     * @When I set basic authentication with :user and :password
45
     */
46
    public function iSetBasicAuthenticationWithAnd($user, $password)
47
    {
48
        $this->getSession()->setBasicAuth($user, $password);
49
    }
50
51
    /**
52
     * Open url with various parameters
53
     *
54
     * @Given (I )am on url composed by:
55
     */
56
    public function iAmOnUrlComposedBy(TableNode $tableNode)
57
    {
58
        $url = '';
59
        foreach ($tableNode->getHash() as $hash) {
60
            $url .= $hash['parameters'];
61
        }
62
63
        return $this->getMinkContext()
64
            ->visit($url);
65
    }
66
67
    /**
68
     * Clicks on the nth CSS element
69
     *
70
     * @When (I )click on the :index :element element
71
     */
72
    public function iClickOnTheNthElement($index, $element)
73
    {
74
        $node = $this->findElement('css', $element, $index);
75
        $node->click();
76
    }
77
78
    /**
79
     * Click on the nth specified link
80
     *
81
     * @When (I )follow the :index :link link
82
     */
83
    public function iFollowTheNthLink($index, $link)
84
    {
85
        $element = ['link', $this->getSession()->getSelectorsHandler()->xpathLiteral($link)];
0 ignored issues
show
Deprecated Code introduced by
The method Behat\Mink\Selector\Sele...Handler::xpathLiteral() has been deprecated with message: since Mink 1.7. Use \Behat\Mink\Selector\Xpath\Escaper::escapeLiteral when building Xpath or pass the unescaped value when using the named selector.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
86
        $node = $this->findElement('named', $element, $index);
87
        $node->click();
88
    }
89
90
    /**
91
     * Presses the nth specified button
92
     *
93
     * @When (I )press the :index :button button
94
     */
95
    public function pressTheNthButton($index, $button)
96
    {
97
        $element = ['button', $this->getSession()->getSelectorsHandler()->xpathLiteral($button)];
0 ignored issues
show
Deprecated Code introduced by
The method Behat\Mink\Selector\Sele...Handler::xpathLiteral() has been deprecated with message: since Mink 1.7. Use \Behat\Mink\Selector\Xpath\Escaper::escapeLiteral when building Xpath or pass the unescaped value when using the named selector.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
98
        $node = $this->findElement('named', $element, $index);
99
        $node->click();
100
    }
101
102
    /**
103
     * Fills in form field with current date
104
     *
105
     * @When (I )fill in :field with the current date
106
     */
107
    public function iFillInWithTheCurrentDate($field)
108
    {
109
        return $this->iFillInWithTheCurrentDateAndModifier($field, 'now');
110
    }
111
112
    /**
113
     * Fills in form field with current date and strtotime modifier
114
     *
115
     * @When (I )fill in :field with the current date and modifier :modifier
116
     */
117
    public function iFillInWithTheCurrentDateAndModifier($field, $modifier)
118
    {
119
        return $this->getMinkContext()
120
            ->fillField($field, date($this->dateFormat, strtotime($modifier)));
121
    }
122
123
    /**
124
     * Mouse over a CSS element
125
     *
126
     * @When (I )hover :element
127
     */
128 View Code Duplication
    public function iHoverIShouldSeeIn($element)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
129
    {
130
        $node = $this->getSession()->getPage()->find('css', $element);
131
        if ($node === null) {
132
            throw new \Exception("The hovered element '$element' was not found anywhere in the page");
133
        }
134
        $node->mouseOver();
135
    }
136
137
    /**
138
     * Save value of the field in parameters array
139
     *
140
     * @When (I )save the value of :field in the :parameter parameter
141
     */
142
    public function iSaveTheValueOfInTheParameter($field, $parameter)
143
    {
144
        $field = str_replace('\\"', '"', $field);
145
        $node  = $this->getSession()->getPage()->findField($field);
146
        if ($node === null) {
147
            throw new \Exception("The field '$field' was not found anywhere in the page");
148
        }
149
150
        $this->setMinkParameter($parameter, $node->getValue());
151
    }
152
153
    /**
154
     * Checks, that the page should contains specified text after given timeout
155
     *
156
     * @Then (I )wait :count second(s) until I see :text
157
     */
158
    public function iWaitSecondsUntilISee($count, $text)
159
    {
160
        $this->iWaitSecondsUntilISeeInTheElement($count, $text, 'html');
161
    }
162
163
    /**
164
     * Checks, that the page should not contain specified text before given timeout
165
     *
166
     * @Then (I )should not see :text within :count second(s)
167
     */
168
    public function iDontSeeInSeconds($count, $text)
169
    {
170
        $caught = false;
171
        try {
172
            $this->iWaitSecondsUntilISee($count, $text);
173
        }
174
        catch (ExpectationException $e) {
175
            $caught = true;
176
        }
177
178
        $this->assertTrue($caught, "Text '$text' has been found");
179
    }
180
181
    /**
182
     * Checks, that the page should contains specified text after timeout
183
     *
184
     * @Then (I )wait until I see :text
185
     */
186
    public function iWaitUntilISee($text)
187
    {
188
        $this->iWaitSecondsUntilISee($this->timeout, $text);
189
    }
190
191
    /**
192
     * Checks, that the element contains specified text after timeout
193
     *
194
     * @Then (I )wait :count second(s) until I see :text in the :element element
195
     */
196
    public function iWaitSecondsUntilISeeInTheElement($count, $text, $element)
197
    {
198
        $startTime = time();
199
        $this->iWaitSecondsForElement($count, $element);
200
201
        $expected = str_replace('\\"', '"', $text);
202
        $message = "The text '$expected' was not found after a $count seconds timeout";
203
204
        $found = false;
205 View Code Duplication
        do {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
206
            try {
207
                usleep(1000);
208
                $node = $this->getSession()->getPage()->find('css', $element);
209
                $this->assertContains($expected, $node->getText(), $message);
210
                return;
211
            }
212
            catch (ExpectationException $e) {
213
                /* Intentionaly leave blank */
214
            }
215
            catch (StaleElementReference $e) {
216
                // assume page reloaded whilst we were still waiting
217
            }
218
        } while (!$found && (time() - $startTime < $count));
219
220
        // final assertion...
221
        $node = $this->getSession()->getPage()->find('css', $element);
222
        $this->assertContains($expected, $node->getText(), $message);
223
    }
224
225
    /**
226
     * @Then (I )wait :count second(s)
227
     */
228
    public function iWaitSeconds($count)
229
    {
230
        usleep($count * 1000000);
231
    }
232
233
    /**
234
     * Checks, that the element contains specified text after timeout
235
     *
236
     * @Then (I )wait until I see :text in the :element element
237
     */
238
    public function iWaitUntilISeeInTheElement($text, $element)
239
    {
240
        $this->iWaitSecondsUntilISeeInTheElement($this->timeout, $text, $element);
241
    }
242
243
    /**
244
     * Checks, that the page should contains specified element after timeout
245
     *
246
     * @Then (I )wait for :element element
247
     */
248
    public function iWaitForElement($element)
249
    {
250
        $this->iWaitSecondsForElement($this->timeout, $element);
251
    }
252
253
    /**
254
     * Wait for a element
255
     *
256
     * @Then (I )wait :count second(s) for :element element
257
     */
258
    public function iWaitSecondsForElement($count, $element)
259
    {
260
        $found = false;
261
        $startTime = time();
262
263 View Code Duplication
        do {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
264
            try {
265
                usleep(1000);
266
                $node = $this->getSession()->getPage()->findAll('css', $element);
267
                $this->assertCount(1, $node);
268
                $found = true;
269
            }
270
            catch (ExpectationException $e) {
271
                /* Intentionnaly leave blank */
272
            }
273
        }
274
        while (!$found && (time() - $startTime < $count));
275
276
        if ($found === false) {
277
            $message = "The element '$element' was not found after a $count seconds timeout";
278
            throw new ResponseTextException($message, $this->getSession(), $e);
279
        }
280
    }
281
282
    /**
283
     * @Then /^(?:|I )should see (?P<count>\d+) "(?P<element>[^"]*)" in the (?P<index>\d+)(?:st|nd|rd|th) "(?P<parent>[^"]*)"$/
284
     */
285 View Code Duplication
    public function iShouldSeeNElementInTheNthParent($count, $element, $index, $parent)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
286
    {
287
        $actual = $this->countElements($element, $index, $parent);
288
        if ($actual !== $count) {
289
            throw new \Exception("$actual occurrences of the '$element' element in '$parent' found");
290
        }
291
    }
292
293
    /**
294
     * @Then (I )should see less than :count :element in the :index :parent
295
     */
296 View Code Duplication
    public function iShouldSeeLessThanNElementInTheNthParent($count, $element, $index, $parent)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
297
    {
298
        $actual = $this->countElements($element, $index, $parent);
299
        if ($actual > $count) {
300
            throw new \Exception("$actual occurrences of the '$element' element in '$parent' found");
301
        }
302
    }
303
304
    /**
305
     * @Then (I )should see more than :count :element in the :index :parent
306
     */
307 View Code Duplication
    public function iShouldSeeMoreThanNElementInTheNthParent($count, $element, $index, $parent)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
308
    {
309
        $actual = $this->countElements($element, $index, $parent);
310
        if ($actual < $count) {
311
            throw new \Exception("$actual occurrences of the '$element' element in '$parent' found");
312
        }
313
    }
314
315
    /**
316
     * Checks, that element with given CSS is enabled
317
     *
318
     * @Then the element :element should be enabled
319
     */
320 View Code Duplication
    public function theElementShouldBeEnabled($element)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
321
    {
322
        $node = $this->getSession()->getPage()->find('css', $element);
323
        if ($node === null) {
324
            throw new \Exception("There is no '$element' element");
325
        }
326
327
        if ($node->hasAttribute('disabled')) {
328
            throw new \Exception("The element '$element' is not enabled");
329
        }
330
    }
331
332
    /**
333
     * Checks, that element with given CSS is disabled
334
     *
335
     * @Then the element :element should be disabled
336
     */
337
    public function theElementShouldBeDisabled($element)
338
    {
339
        $this->not(function () use($element) {
340
            $this->theElementShouldBeEnabled($element);
341
        }, "The element '$element' is not disabled");
342
    }
343
344
    /**
345
     * Checks, that given select box contains the specified option
346
     *
347
     * @Then the :select select box should contain :option
348
     */
349
    public function theSelectBoxShouldContain($select, $option)
350
    {
351
        $select = str_replace('\\"', '"', $select);
352
        $option = str_replace('\\"', '"', $option);
353
354
        $obj = $this->getSession()->getPage()->findField($select);
355
        if ($obj === null) {
356
            throw new ElementNotFoundException(
357
                $this->getSession(), 'select box', 'id|name|label|value', $select
358
            );
359
        }
360
        $optionText = $obj->getText();
361
362
        $message = "The '$select' select box does not contain the '$option' option";
363
        $this->assertContains($option, $optionText, $message);
364
    }
365
366
    /**
367
     * Checks, that given select box does not contain the specified option
368
     *
369
     * @Then the :select select box should not contain :option
370
     */
371
    public function theSelectBoxShouldNotContain($select, $option)
372
    {
373
        $this->not(function () use($select, $option) {
374
            $this->theSelectBoxShouldContain($select, $option);
375
        }, "The '$select' select box does contain the '$option' option");
376
    }
377
378
    /**
379
     * Checks, that the specified CSS element is visible
380
     *
381
     * @Then the :element element should be visible
382
     */
383
    public function theElementShouldBeVisible($element)
384
    {
385
        $displayedNode = $this->getSession()->getPage()->find('css', $element);
386
        if ($displayedNode === null) {
387
            throw new \Exception("The element '$element' was not found anywhere in the page");
388
        }
389
390
391
        $message = "The element '$element' is not visible";
392
        $this->assertTrue($displayedNode->isVisible(), $message);
393
    }
394
395
    /**
396
     * Checks, that the specified CSS element is not visible
397
     *
398
     * @Then the :element element should not be visible
399
     */
400
    public function theElementShouldNotBeVisible($element)
401
    {
402
        $exception = new \Exception("The element '$element' is visible");
403
404
        $this->not(function () use($element) {
405
            $this->theElementShouldBeVisible($element);
406
        }, $exception);
407
    }
408
409
    /**
410
     * Select a frame by its name or ID.
411
     *
412
     * @When (I )switch to iframe :name
413
     * @When (I )switch to frame :name
414
     */
415
    public function switchToIFrame($name)
416
    {
417
        $this->getSession()->switchToIFrame($name);
418
    }
419
420
    /**
421
     * Go back to main document frame.
422
     *
423
     * @When (I )switch to main frame
424
     */
425
    public function switchToMainFrame()
426
    {
427
        $this->getSession()->switchToIFrame();
428
    }
429
430
    /**
431
     * test time from when the scenario started
432
     *
433
     * @Then (the )total elapsed time should be :comparison than :expected seconds
434
     * @Then (the )total elapsed time should be :comparison to :expected seconds
435
     */
436
    public function elapsedTime($comparison, $expected)
437
    {
438
        $elapsed = time() - $this->timerStartedAt;
439
440
        switch ($comparison) {
441
            case 'less':
442
                $this->assertTrue($elapsed < $expected, "Elapsed time '$elapsed' is not less than '$expected' seconds.");
443
                break;
444
445
            case 'more':
446
                $this->assertTrue($elapsed > $expected, "Elapsed time '$elapsed' is not more than '$expected' seconds.");
447
                break;
448
449
            case 'equal':
450
                $this->assertTrue($elapsed === $expected, "Elapsed time '$elapsed' is not '$expected' seconds.");
451
                break;
452
453
            default:
454
                throw new PendingException("Unknown comparison '$comparison'. Use 'less', 'more' or 'equal'");
455
        }
456
    }
457
}
458