Passed
Push — master ( 20c591...cbb96e )
by San
01:29
created

BrowserContext   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 444
Duplicated Lines 14.86 %

Coupling/Cohesion

Components 4
Dependencies 9

Importance

Changes 0
Metric Value
wmc 54
lcom 4
cbo 9
dl 66
loc 444
rs 6.8539
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
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
        $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
                /* Intentionaly 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
261 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...
262
            try {
263
                usleep(1000);
264
                $node = $this->getSession()->getPage()->findAll('css', $element);
265
                $this->assertCount(1, $node);
266
                $found = true;
267
            }
268
            catch (ExpectationException $e) {
269
                /* Intentionnaly leave blank */
270
            }
271
        }
272
        while (!$found && (time() - $startTime < $count));
273
274
        if ($found === false) {
275
            $message = "The element '$element' was not found after a $count seconds timeout";
276
            throw new ResponseTextException($message, $this->getSession(), $e);
277
        }
278
    }
279
280
    /**
281
     * @Then /^(?:|I )should see (?P<count>\d+) "(?P<element>[^"]*)" in the (?P<index>\d+)(?:st|nd|rd|th) "(?P<parent>[^"]*)"$/
282
     */
283 View Code Duplication
    public function iShouldSeeNElementInTheNthParent($count, $element, $index, $parent)
284
    {
285
        $actual = $this->countElements($element, $index, $parent);
286
        if ($actual !== $count) {
287
            throw new \Exception("$actual occurrences of the '$element' element in '$parent' found");
288
        }
289
    }
290
291
    /**
292
     * @Then (I )should see less than :count :element in the :index :parent
293
     */
294 View Code Duplication
    public function iShouldSeeLessThanNElementInTheNthParent($count, $element, $index, $parent)
295
    {
296
        $actual = $this->countElements($element, $index, $parent);
297
        if ($actual > $count) {
298
            throw new \Exception("$actual occurrences of the '$element' element in '$parent' found");
299
        }
300
    }
301
302
    /**
303
     * @Then (I )should see more than :count :element in the :index :parent
304
     */
305 View Code Duplication
    public function iShouldSeeMoreThanNElementInTheNthParent($count, $element, $index, $parent)
306
    {
307
        $actual = $this->countElements($element, $index, $parent);
308
        if ($actual < $count) {
309
            throw new \Exception("$actual occurrences of the '$element' element in '$parent' found");
310
        }
311
    }
312
313
    /**
314
     * Checks, that element with given CSS is enabled
315
     *
316
     * @Then the element :element should be enabled
317
     */
318 View Code Duplication
    public function theElementShouldBeEnabled($element)
319
    {
320
        $node = $this->getSession()->getPage()->find('css', $element);
321
        if ($node === null) {
322
            throw new \Exception("There is no '$element' element");
323
        }
324
325
        if ($node->hasAttribute('disabled')) {
326
            throw new \Exception("The element '$element' is not enabled");
327
        }
328
    }
329
330
    /**
331
     * Checks, that element with given CSS is disabled
332
     *
333
     * @Then the element :element should be disabled
334
     */
335
    public function theElementShouldBeDisabled($element)
336
    {
337
        $this->not(function () use($element) {
338
            $this->theElementShouldBeEnabled($element);
339
        }, "The element '$element' is not disabled");
340
    }
341
342
    /**
343
     * Checks, that given select box contains the specified option
344
     *
345
     * @Then the :select select box should contain :option
346
     */
347
    public function theSelectBoxShouldContain($select, $option)
348
    {
349
        $select = str_replace('\\"', '"', $select);
350
        $option = str_replace('\\"', '"', $option);
351
352
        $obj = $this->getSession()->getPage()->findField($select);
353
        if ($obj === null) {
354
            throw new ElementNotFoundException(
355
                $this->getSession(), 'select box', 'id|name|label|value', $select
356
            );
357
        }
358
        $optionText = $obj->getText();
359
360
        $message = "The '$select' select box does not contain the '$option' option";
361
        $this->assertContains($option, $optionText, $message);
362
    }
363
364
    /**
365
     * Checks, that given select box does not contain the specified option
366
     *
367
     * @Then the :select select box should not contain :option
368
     */
369
    public function theSelectBoxShouldNotContain($select, $option)
370
    {
371
        $this->not(function () use($select, $option) {
372
            $this->theSelectBoxShouldContain($select, $option);
373
        }, "The '$select' select box does contain the '$option' option");
374
    }
375
376
    /**
377
     * Checks, that the specified CSS element is visible
378
     *
379
     * @Then the :element element should be visible
380
     */
381
    public function theElementShouldBeVisible($element)
382
    {
383
        $displayedNode = $this->getSession()->getPage()->find('css', $element);
384
        if ($displayedNode === null) {
385
            throw new \Exception("The element '$element' was not found anywhere in the page");
386
        }
387
388
389
        $message = "The element '$element' is not visible";
390
        $this->assertTrue($displayedNode->isVisible(), $message);
391
    }
392
393
    /**
394
     * Checks, that the specified CSS element is not visible
395
     *
396
     * @Then the :element element should not be visible
397
     */
398
    public function theElementShouldNotBeVisible($element)
399
    {
400
        $exception = new \Exception("The element '$element' is visible");
401
402
        $this->not(function () use($element) {
403
            $this->theElementShouldBeVisible($element);
404
        }, $exception);
405
    }
406
407
    /**
408
     * Select a frame by its name or ID.
409
     *
410
     * @When (I )switch to iframe :name
411
     * @When (I )switch to frame :name
412
     */
413
    public function switchToIFrame($name)
414
    {
415
        $this->getSession()->switchToIFrame($name);
416
    }
417
418
    /**
419
     * Go back to main document frame.
420
     *
421
     * @When (I )switch to main frame
422
     */
423
    public function switchToMainFrame()
424
    {
425
        $this->getSession()->switchToIFrame();
426
    }
427
428
    /**
429
     * test time from when the scenario started
430
     *
431
     * @Then (the )total elapsed time should be :comparison than :expected seconds
432
     * @Then (the )total elapsed time should be :comparison to :expected seconds
433
     */
434
    public function elapsedTime($comparison, $expected)
435
    {
436
        $elapsed = time() - $this->timerStartedAt;
437
438
        switch ($comparison) {
439
            case 'less':
440
                $this->assertTrue($elapsed < $expected, "Elapsed time '$elapsed' is not less than '$expected' seconds.");
441
                break;
442
443
            case 'more':
444
                $this->assertTrue($elapsed > $expected, "Elapsed time '$elapsed' is not more than '$expected' seconds.");
445
                break;
446
447
            case 'equal':
448
                $this->assertTrue($elapsed === $expected, "Elapsed time '$elapsed' is not '$expected' seconds.");
449
                break;
450
451
            default:
452
                throw new PendingException("Unknown comparison '$comparison'. Use 'less', 'more' or 'equal'");
453
        }
454
    }
455
}
456