BrowserContext   C
last analyzed

Complexity

Total Complexity 54

Size/Duplication

Total Lines 445
Duplicated Lines 14.83 %

Coupling/Cohesion

Components 4
Dependencies 9

Importance

Changes 0
Metric Value
wmc 54
lcom 4
cbo 9
dl 66
loc 445
rs 6.4799
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 5 1
A pressTheNthButton() 0 5 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
A iWaitSecondsUntilISeeInTheElement() 14 28 5
A iWaitSeconds() 0 4 1
A iWaitUntilISeeInTheElement() 0 4 1
A iWaitForElement() 0 4 1
A iWaitSecondsForElement() 12 24 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
        $node = $this->findElement('named', ['link', $link], $index);
86
        $node->click();
87
    }
88
89
    /**
90
     * Presses the nth specified button
91
     *
92
     * @When (I )press the :index :button button
93
     */
94
    public function pressTheNthButton($index, $button)
95
    {
96
        $node = $this->findElement('named', ['button', $button], $index);
97
        $node->click();
98
    }
99
100
    /**
101
     * Fills in form field with current date
102
     *
103
     * @When (I )fill in :field with the current date
104
     */
105
    public function iFillInWithTheCurrentDate($field)
106
    {
107
        return $this->iFillInWithTheCurrentDateAndModifier($field, 'now');
108
    }
109
110
    /**
111
     * Fills in form field with current date and strtotime modifier
112
     *
113
     * @When (I )fill in :field with the current date and modifier :modifier
114
     */
115
    public function iFillInWithTheCurrentDateAndModifier($field, $modifier)
116
    {
117
        return $this->getMinkContext()
118
            ->fillField($field, date($this->dateFormat, strtotime($modifier)));
119
    }
120
121
    /**
122
     * Mouse over a CSS element
123
     *
124
     * @When (I )hover :element
125
     */
126 View Code Duplication
    public function iHoverIShouldSeeIn($element)
127
    {
128
        $node = $this->getSession()->getPage()->find('css', $element);
129
        if ($node === null) {
130
            throw new \Exception("The hovered element '$element' was not found anywhere in the page");
131
        }
132
        $node->mouseOver();
133
    }
134
135
    /**
136
     * Save value of the field in parameters array
137
     *
138
     * @When (I )save the value of :field in the :parameter parameter
139
     */
140
    public function iSaveTheValueOfInTheParameter($field, $parameter)
141
    {
142
        $field = str_replace('\\"', '"', $field);
143
        $node  = $this->getSession()->getPage()->findField($field);
144
        if ($node === null) {
145
            throw new \Exception("The field '$field' was not found anywhere in the page");
146
        }
147
148
        $this->setMinkParameter($parameter, $node->getValue());
149
    }
150
151
    /**
152
     * Checks, that the page should contains specified text after given timeout
153
     *
154
     * @Then (I )wait :count second(s) until I see :text
155
     */
156
    public function iWaitSecondsUntilISee($count, $text)
157
    {
158
        $this->iWaitSecondsUntilISeeInTheElement($count, $text, 'html');
159
    }
160
161
    /**
162
     * Checks, that the page should not contain specified text before given timeout
163
     *
164
     * @Then (I )should not see :text within :count second(s)
165
     */
166
    public function iDontSeeInSeconds($count, $text)
167
    {
168
        $caught = false;
169
        try {
170
            $this->iWaitSecondsUntilISee($count, $text);
171
        }
172
        catch (ExpectationException $e) {
173
            $caught = true;
174
        }
175
176
        $this->assertTrue($caught, "Text '$text' has been found");
177
    }
178
179
    /**
180
     * Checks, that the page should contains specified text after timeout
181
     *
182
     * @Then (I )wait until I see :text
183
     */
184
    public function iWaitUntilISee($text)
185
    {
186
        $this->iWaitSecondsUntilISee($this->timeout, $text);
187
    }
188
189
    /**
190
     * Checks, that the element contains specified text after timeout
191
     *
192
     * @Then (I )wait :count second(s) until I see :text in the :element element
193
     */
194
    public function iWaitSecondsUntilISeeInTheElement($count, $text, $element)
195
    {
196
        $startTime = time();
197
        $this->iWaitSecondsForElement($count, $element);
198
199
        $expected = str_replace('\\"', '"', $text);
200
        $message = "The text '$expected' was not found after a $count seconds timeout";
201
202
        $found = false;
203 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...
204
            try {
205
                usleep(1000);
206
                $node = $this->getSession()->getPage()->find('css', $element);
207
                $this->assertContains($expected, $node->getText(), $message);
208
                return;
209
            }
210
            catch (ExpectationException $e) {
211
                /* Intentionally leave blank */
212
            }
213
            catch (StaleElementReference $e) {
214
                // assume page reloaded whilst we were still waiting
215
            }
216
        } while (!$found && (time() - $startTime < $count));
217
218
        // final assertion...
219
        $node = $this->getSession()->getPage()->find('css', $element);
220
        $this->assertContains($expected, $node->getText(), $message);
221
    }
222
223
    /**
224
     * @Then (I )wait :count second(s)
225
     */
226
    public function iWaitSeconds($count)
227
    {
228
        usleep($count * 1000000);
229
    }
230
231
    /**
232
     * Checks, that the element contains specified text after timeout
233
     *
234
     * @Then (I )wait until I see :text in the :element element
235
     */
236
    public function iWaitUntilISeeInTheElement($text, $element)
237
    {
238
        $this->iWaitSecondsUntilISeeInTheElement($this->timeout, $text, $element);
239
    }
240
241
    /**
242
     * Checks, that the page should contains specified element after timeout
243
     *
244
     * @Then (I )wait for :element element
245
     */
246
    public function iWaitForElement($element)
247
    {
248
        $this->iWaitSecondsForElement($this->timeout, $element);
249
    }
250
251
    /**
252
     * Wait for a element
253
     *
254
     * @Then (I )wait :count second(s) for :element element
255
     */
256
    public function iWaitSecondsForElement($count, $element)
257
    {
258
        $found = false;
259
        $startTime = time();
260
        $e = null;
261
262 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...
263
            try {
264
                usleep(1000);
265
                $node = $this->getSession()->getPage()->findAll('css', $element);
266
                $this->assertCount(1, $node);
267
                $found = true;
268
            }
269
            catch (ExpectationException $e) {
270
                /* Intentionally leave blank */
271
            }
272
        }
273
        while (!$found && (time() - $startTime < $count));
274
275
        if ($found === false) {
276
            $message = "The element '$element' was not found after a $count seconds timeout";
277
            throw new ResponseTextException($message, $this->getSession()->getDriver(), $e);
278
        }
279
    }
280
281
    /**
282
     * @Then /^(?:|I )should see (?P<count>\d+) "(?P<element>[^"]*)" in the (?P<index>\d+)(?:st|nd|rd|th) "(?P<parent>[^"]*)"$/
283
     */
284 View Code Duplication
    public function iShouldSeeNElementInTheNthParent($count, $element, $index, $parent)
285
    {
286
        $actual = $this->countElements($element, $index, $parent);
287
        if ($actual !== $count) {
288
            throw new \Exception("$actual occurrences of the '$element' element in '$parent' found");
289
        }
290
    }
291
292
    /**
293
     * @Then (I )should see less than :count :element in the :index :parent
294
     */
295 View Code Duplication
    public function iShouldSeeLessThanNElementInTheNthParent($count, $element, $index, $parent)
296
    {
297
        $actual = $this->countElements($element, $index, $parent);
298
        if ($actual > $count) {
299
            throw new \Exception("$actual occurrences of the '$element' element in '$parent' found");
300
        }
301
    }
302
303
    /**
304
     * @Then (I )should see more than :count :element in the :index :parent
305
     */
306 View Code Duplication
    public function iShouldSeeMoreThanNElementInTheNthParent($count, $element, $index, $parent)
307
    {
308
        $actual = $this->countElements($element, $index, $parent);
309
        if ($actual < $count) {
310
            throw new \Exception("$actual occurrences of the '$element' element in '$parent' found");
311
        }
312
    }
313
314
    /**
315
     * Checks, that element with given CSS is enabled
316
     *
317
     * @Then the element :element should be enabled
318
     */
319 View Code Duplication
    public function theElementShouldBeEnabled($element)
320
    {
321
        $node = $this->getSession()->getPage()->find('css', $element);
322
        if ($node === null) {
323
            throw new \Exception("There is no '$element' element");
324
        }
325
326
        if ($node->hasAttribute('disabled')) {
327
            throw new \Exception("The element '$element' is not enabled");
328
        }
329
    }
330
331
    /**
332
     * Checks, that element with given CSS is disabled
333
     *
334
     * @Then the element :element should be disabled
335
     */
336
    public function theElementShouldBeDisabled($element)
337
    {
338
        $this->not(function () use($element) {
339
            $this->theElementShouldBeEnabled($element);
340
        }, "The element '$element' is not disabled");
341
    }
342
343
    /**
344
     * Checks, that given select box contains the specified option
345
     *
346
     * @Then the :select select box should contain :option
347
     */
348
    public function theSelectBoxShouldContain($select, $option)
349
    {
350
        $select = str_replace('\\"', '"', $select);
351
        $option = str_replace('\\"', '"', $option);
352
353
        $obj = $this->getSession()->getPage()->findField($select);
354
        if ($obj === null) {
355
            throw new ElementNotFoundException(
356
                $this->getSession()->getDriver(), 'select box', 'id|name|label|value', $select
357
            );
358
        }
359
        $optionText = $obj->getText();
360
361
        $message = "The '$select' select box does not contain the '$option' option";
362
        $this->assertContains($option, $optionText, $message);
363
    }
364
365
    /**
366
     * Checks, that given select box does not contain the specified option
367
     *
368
     * @Then the :select select box should not contain :option
369
     */
370
    public function theSelectBoxShouldNotContain($select, $option)
371
    {
372
        $this->not(function () use($select, $option) {
373
            $this->theSelectBoxShouldContain($select, $option);
374
        }, "The '$select' select box does contain the '$option' option");
375
    }
376
377
    /**
378
     * Checks, that the specified CSS element is visible
379
     *
380
     * @Then the :element element should be visible
381
     */
382
    public function theElementShouldBeVisible($element)
383
    {
384
        $displayedNode = $this->getSession()->getPage()->find('css', $element);
385
        if ($displayedNode === null) {
386
            throw new \Exception("The element '$element' was not found anywhere in the page");
387
        }
388
389
390
        $message = "The element '$element' is not visible";
391
        $this->assertTrue($displayedNode->isVisible(), $message);
392
    }
393
394
    /**
395
     * Checks, that the specified CSS element is not visible
396
     *
397
     * @Then the :element element should not be visible
398
     */
399
    public function theElementShouldNotBeVisible($element)
400
    {
401
        $exception = new \Exception("The element '$element' is visible");
402
403
        $this->not(function () use($element) {
404
            $this->theElementShouldBeVisible($element);
405
        }, $exception);
406
    }
407
408
    /**
409
     * Select a frame by its name or ID.
410
     *
411
     * @When (I )switch to iframe :name
412
     * @When (I )switch to frame :name
413
     */
414
    public function switchToIFrame($name)
415
    {
416
        $this->getSession()->switchToIFrame($name);
417
    }
418
419
    /**
420
     * Go back to main document frame.
421
     *
422
     * @When (I )switch to main frame
423
     */
424
    public function switchToMainFrame()
425
    {
426
        $this->getSession()->switchToIFrame();
427
    }
428
429
    /**
430
     * test time from when the scenario started
431
     *
432
     * @Then (the )total elapsed time should be :comparison than :expected seconds
433
     * @Then (the )total elapsed time should be :comparison to :expected seconds
434
     */
435
    public function elapsedTime($comparison, $expected)
436
    {
437
        $elapsed = time() - $this->timerStartedAt;
438
439
        switch ($comparison) {
440
            case 'less':
441
                $this->assertTrue($elapsed < $expected, "Elapsed time '$elapsed' is not less than '$expected' seconds.");
442
                break;
443
444
            case 'more':
445
                $this->assertTrue($elapsed > $expected, "Elapsed time '$elapsed' is not more than '$expected' seconds.");
446
                break;
447
448
            case 'equal':
449
                $this->assertTrue($elapsed === $expected, "Elapsed time '$elapsed' is not '$expected' seconds.");
450
                break;
451
452
            default:
453
                throw new PendingException("Unknown comparison '$comparison'. Use 'less', 'more' or 'equal'");
454
        }
455
    }
456
}
457