Completed
Pull Request — master (#172)
by Gordon
03:22
created

BrowserContext   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 393
Duplicated Lines 17.3 %

Coupling/Cohesion

Components 3
Dependencies 9

Importance

Changes 18
Bugs 4 Features 0
Metric Value
wmc 45
c 18
b 4
f 0
lcom 3
cbo 9
dl 68
loc 393
rs 8.3673

29 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A closeBrowser() 0 4 1
A iSetBasicAuthenticationWithAnd() 0 4 1
A iAmOnUrlComposedBy() 0 10 2
A iClickOnTheNthElement() 0 12 2
A iFollowTheNthLink() 14 14 2
A pressTheNthButton() 14 14 2
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 iWaitUntilISee() 0 4 1
A iWaitSecondsUntilISeeInTheElement() 0 10 1
A iWaitSeconds() 0 4 1
A iWaitUntilISeeInTheElement() 0 4 1
A iWaitForElement() 0 4 1
B iWaitSecondsForElement() 0 22 4
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

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 Sanpi\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
10
class BrowserContext extends BaseContext
11
{
12
    private $timeout;
13
    private $dateFormat = 'dmYHi';
14
15
    public function __construct($timeout = 1)
16
    {
17
        $this->timeout = $timeout;
18
    }
19
20
    /**
21
     * @AfterScenario
22
     */
23
    public function closeBrowser()
24
    {
25
        $this->getSession()->stop();
26
    }
27
28
    /**
29
     * Set login / password for next HTTP authentication
30
     *
31
     * @When I set basic authentication with :user and :password
32
     */
33
    public function iSetBasicAuthenticationWithAnd($user, $password)
34
    {
35
        $this->getSession()->setBasicAuth($user, $password);
36
    }
37
38
    /**
39
     * Open url with various parameters
40
     *
41
     * @Given (I )am on url composed by:
42
     */
43
    public function iAmOnUrlComposedBy(TableNode $tableNode)
44
    {
45
        $url = '';
46
        foreach ($tableNode->getHash() as $hash) {
47
            $url .= $hash['parameters'];
48
        }
49
50
        return $this->getMinkContext()
51
            ->visit($url);
52
    }
53
54
    /**
55
     * Clicks on the nth CSS element
56
     *
57
     * @When (I )click on the :index :element element
58
     */
59
    public function iClickOnTheNthElement($index, $element)
60
    {
61
        $page = $this->getSession()->getPage();
62
63
        $nodes = $page->findAll('css', $element);
64
65
        if (!isset($nodes[$index - 1])) {
66
            throw new \Exception("The $index element '$element' was not found anywhere in the page");
67
        }
68
69
        $nodes[$index - 1]->click();
70
    }
71
72
    /**
73
     * Click on the nth specified link
74
     *
75
     * @When (I )follow the :index :link link
76
     */
77 View Code Duplication
    public function iFollowTheNthLink($index, $link)
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...
78
    {
79
        $page = $this->getSession()->getPage();
80
81
        $links = $page->findAll('named', [
82
            '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...
83
        ]);
84
85
        if (!isset($links[$index - 1])) {
86
            throw new \Exception("The $index link '$link' was not found anywhere in the page");
87
        }
88
89
        $links[$index - 1]->click();
90
    }
91
92
    /**
93
     * Presses the nth specified button
94
     *
95
     * @When (I )press the :index :button
96
     */
97 View Code Duplication
    public function pressTheNthButton($index, $button)
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...
98
    {
99
        $page = $this->getSession()->getPage();
100
101
        $buttons = $page->findAll('named', [
102
            '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...
103
        ]);
104
105
        if (!isset($buttons[$index - 1])) {
106
            throw new \Exception("The $index button '$button' was not found anywhere in the page");
107
        }
108
109
        $buttons[$index - 1]->press();
110
    }
111
112
    /**
113
     * Fills in form field with current date
114
     *
115
     * @When (I )fill in :field with the current date
116
     */
117
    public function iFillInWithTheCurrentDate($field)
118
    {
119
        return $this->iFillInWithTheCurrentDateAndModifier($field, 'now');
120
    }
121
122
    /**
123
     * Fills in form field with current date and strtotime modifier
124
     *
125
     * @When (I )fill in :field with the current date and modifier :modifier
126
     */
127
    public function iFillInWithTheCurrentDateAndModifier($field, $modifier)
128
    {
129
        return $this->getMinkContext()
130
            ->fillField($field, date($this->dateFormat, strtotime($modifier)));
131
    }
132
133
    /**
134
     * Mouse over a CSS element
135
     *
136
     * @When (I )hover :element
137
     */
138 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...
139
    {
140
        $node = $this->getSession()->getPage()->find('css', $element);
141
        if ($node === null) {
142
            throw new \Exception("The hovered element '$element' was not found anywhere in the page");
143
        }
144
        $node->mouseOver();
145
    }
146
147
    /**
148
     * Save value of the field in parameters array
149
     *
150
     * @When (I )save the value of :field in the :parameter parameter
151
     */
152
    public function iSaveTheValueOfInTheParameter($field, $parameter)
153
    {
154
        $field = str_replace('\\"', '"', $field);
155
        $node  = $this->getSession()->getPage()->findField($field);
156
        if ($node === null) {
157
            throw new \Exception("The field '$field' was not found anywhere in the page");
158
        }
159
160
        $this->setMinkParameter($parameter, $node->getValue());
161
    }
162
163
    /**
164
     * Checks, that the page should contains specified text after given timeout
165
     *
166
     * @Then (I )wait :count second(s) until I see :text
167
     */
168
    public function iWaitSecondsUntilISee($count, $text)
169
    {
170
        $this->iWaitSecondsUntilISeeInTheElement($count, $text, 'html');
171
    }
172
173
    /**
174
     * Checks, that the page should contains specified text after timeout
175
     *
176
     * @Then (I )wait until I see :text
177
     */
178
    public function iWaitUntilISee($text)
179
    {
180
        $this->iWaitSecondsUntilISee($this->timeout, $text);
181
    }
182
183
    /**
184
     * Checks, that the element contains specified text after timeout
185
     *
186
     * @Then (I )wait :count second(s) until I see :text in the :element element
187
     */
188
    public function iWaitSecondsUntilISeeInTheElement($count, $text, $element)
189
    {
190
        $this->iWaitSecondsForElement($count, $element);
191
192
        $expected = str_replace('\\"', '"', $text);
193
        $node = $this->getSession()->getPage()->find('css', $element);
194
        $message = "The text '$expected' was not found after a $count seconds timeout";
195
196
        $this->assertContains($expected, $node->getText(), $message);
197
    }
198
199
    /**
200
     * @Then (I )wait :count second(s)
201
     */
202
    public function iWaitSeconds($count)
203
    {
204
        sleep($count);
205
    }
206
207
    /**
208
     * Checks, that the element contains specified text after timeout
209
     *
210
     * @Then (I )wait until I see :text in the :element element
211
     */
212
    public function iWaitUntilISeeInTheElement($text, $element)
213
    {
214
        $this->iWaitSecondsUntilISeeInTheElement($this->timeout, $text, $element);
215
    }
216
217
    /**
218
     * Checks, that the page should contains specified element after timeout
219
     *
220
     * @Then (I )wait for :element element
221
     */
222
    public function iWaitForElement($element)
223
    {
224
        $this->iWaitSecondsForElement($this->timeout, $element);
225
    }
226
227
    /**
228
     * Wait for a element
229
     *
230
     * @Then (I )wait :count second(s) for :element element
231
     */
232
    public function iWaitSecondsForElement($count, $element)
233
    {
234
        $found = false;
235
        $startTime = time();
236
237
        do {
238
            try {
239
                $node = $this->getSession()->getPage()->findAll('css', $element);
240
                $this->assertCount(1, $node);
241
                $found = true;
242
            }
243
            catch (ExpectationException $e) {
244
                /* Intentionnaly leave blank */
245
            }
246
        }
247
        while (time() - $startTime < $count);
248
249
        if ($found === false) {
250
            $message = "The element '$element' was not found after a $count seconds timeout";
251
            throw new ResponseTextException($message, $this->getSession(), $e);
252
        }
253
    }
254
255
    /**
256
     * @Then /^(?:|I )should see (?P<count>\d+) "(?P<element>[^"]*)" in the (?P<index>\d+)(?:st|nd|rd|th) "(?P<parent>[^"]*)"$/
257
     */
258 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...
259
    {
260
        $actual = $this->countElements($element, $index, $parent);
261
        if ($actual !== $count) {
262
            throw new \Exception("$actual occurrences of the '$element' element in '$parent' found");
263
        }
264
    }
265
266
    /**
267
     * @Then (I )should see less than :count :element in the :index :parent
268
     */
269 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...
270
    {
271
        $actual = $this->countElements($element, $index, $parent);
272
        if ($actual > $count) {
273
            throw new \Exception("$actual occurrences of the '$element' element in '$parent' found");
274
        }
275
    }
276
277
    /**
278
     * @Then (I )should see more than :count :element in the :index :parent
279
     */
280 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...
281
    {
282
        $actual = $this->countElements($element, $index, $parent);
283
        if ($actual < $count) {
284
            throw new \Exception("$actual occurrences of the '$element' element in '$parent' found");
285
        }
286
    }
287
288
    /**
289
     * Checks, that element with given CSS is enabled
290
     *
291
     * @Then the element :element should be enabled
292
     */
293 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...
294
    {
295
        $node = $this->getSession()->getPage()->find('css', $element);
296
        if ($node === null) {
297
            throw new \Exception("There is no '$element' element");
298
        }
299
300
        if ($node->hasAttribute('disabled')) {
301
            throw new \Exception("The element '$element' is not enabled");
302
        }
303
    }
304
305
    /**
306
     * Checks, that element with given CSS is disabled
307
     *
308
     * @Then the element :element should be disabled
309
     */
310
    public function theElementShouldBeDisabled($element)
311
    {
312
        $this->not(function () use($element) {
313
            $this->theElementShouldBeEnabled($element);
314
        }, "The element '$element' is not disabled");
315
    }
316
317
    /**
318
     * Checks, that given select box contains the specified option
319
     *
320
     * @Then the :select select box should contain :option
321
     */
322
    public function theSelectBoxShouldContain($select, $option)
323
    {
324
        $select = str_replace('\\"', '"', $select);
325
        $option = str_replace('\\"', '"', $option);
326
327
        $obj = $this->getSession()->getPage()->findField($select);
328
        if ($obj === null) {
329
            throw new ElementNotFoundException(
330
                $this->getSession(), 'select box', 'id|name|label|value', $select
331
            );
332
        }
333
        $optionText = $obj->getText();
334
335
        $message = "The '$select' select box does not contain the '$option' option";
336
        $this->assertContains($option, $optionText, $message);
337
    }
338
339
    /**
340
     * Checks, that given select box does not contain the specified option
341
     *
342
     * @Then the :select select box should not contain :option
343
     */
344
    public function theSelectBoxShouldNotContain($select, $option)
345
    {
346
        $this->not(function () use($select, $option) {
347
            $this->theSelectBoxShouldContain($select, $option);
348
        }, "The '$select' select box does contain the '$option' option");
349
    }
350
351
    /**
352
     * Checks, that the specified CSS element is visible
353
     *
354
     * @Then the :element element should be visible
355
     */
356
    public function theElementShouldBeVisible($element)
357
    {
358
        $displayedNode = $this->getSession()->getPage()->find('css', $element);
359
        if ($displayedNode === null) {
360
            throw new \Exception("The element '$element' was not found anywhere in the page");
361
        }
362
363
364
        $message = "The element '$element' is not visible";
365
        $this->assertTrue($displayedNode->isVisible(), $message);
366
    }
367
368
    /**
369
     * Checks, that the specified CSS element is not visible
370
     *
371
     * @Then the :element element should not be visible
372
     */
373
    public function theElementShouldNotBeVisible($element)
374
    {
375
        $exception = new \Exception("The element '$element' is visible");
376
377
        $this->not(function () use($element) {
378
            $this->theElementShouldBeVisible($element);
379
        }, $exception);
380
    }
381
382
    /**
383
     * Select a frame by its name or ID.
384
     *
385
     * @When (I )switch to iframe :name
386
     * @When (I )switch to frame :name
387
     */
388
    public function switchToIFrame($name)
389
    {
390
        $this->getSession()->switchToIFrame($name);
391
    }
392
393
    /**
394
     * Go back to main document frame.
395
     *
396
     * @When (I )switch to main frame
397
     */
398
    public function switchToMainFrame()
399
    {
400
        $this->getSession()->switchToIFrame();
401
    }
402
}
403