Completed
Push — master ( 52cd2c...047880 )
by Kamil
47:26 queued 26:32
created

ShowPage   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 250
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
wmc 43
lcom 1
cbo 9
dl 0
loc 250
rs 8.96
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A getRouteName() 0 4 1
A addToCart() 0 8 2
A addToCartWithQuantity() 0 9 2
A addToCartWithVariant() 0 10 2
A addToCartWithOption() 0 7 1
A getAttributeByName() 0 26 5
A getAttributes() 0 6 1
A getAverageRating() 0 4 1
A getCurrentUrl() 0 4 1
A getCurrentVariantName() 0 6 1
A getName() 0 4 1
A getPrice() 0 4 1
A hasAddToCartButton() 0 8 3
A hasAssociation() 0 10 2
A hasProductInAssociation() 0 8 1
A hasProductOutOfStockValidationMessage() 0 10 2
A hasReviewTitled() 0 10 2
A isOutOfStock() 0 4 1
A isMainImageDisplayed() 0 13 2
A countReviews() 0 4 1
A selectOption() 0 5 1
A selectVariant() 0 13 2
A visit() 0 5 1
A open() 0 18 5
A getDefinedElements() 0 23 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sylius\Behat\Page\Shop\Product;
15
16
use Behat\Mink\Driver\Selenium2Driver;
17
use Behat\Mink\Exception\ElementNotFoundException;
18
use FriendsOfBehat\PageObjectExtension\Page\SymfonyPage;
19
use FriendsOfBehat\PageObjectExtension\Page\UnexpectedPageException;
20
use Sylius\Behat\Service\JQueryHelper;
21
use Sylius\Component\Product\Model\ProductInterface;
22
use Sylius\Component\Product\Model\ProductOptionInterface;
23
use Webmozart\Assert\Assert;
24
25
class ShowPage extends SymfonyPage implements ShowPageInterface
26
{
27
    public function getRouteName(): string
28
    {
29
        return 'sylius_shop_product_show';
30
    }
31
32
    public function addToCart(): void
33
    {
34
        $this->getElement('add_to_cart_button')->click();
35
36
        if ($this->getDriver() instanceof Selenium2Driver) {
37
            JQueryHelper::waitForAsynchronousActionsToFinish($this->getSession());
38
        }
39
    }
40
41
    public function addToCartWithQuantity(string $quantity): void
42
    {
43
        $this->getElement('quantity')->setValue($quantity);
44
        $this->getElement('add_to_cart_button')->click();
45
46
        if ($this->getDriver() instanceof Selenium2Driver) {
47
            JQueryHelper::waitForAsynchronousActionsToFinish($this->getSession());
48
        }
49
    }
50
51
    public function addToCartWithVariant(string $variant): void
52
    {
53
        $this->selectVariant($variant);
54
55
        $this->getElement('add_to_cart_button')->click();
56
57
        if ($this->getDriver() instanceof Selenium2Driver) {
58
            JQueryHelper::waitForAsynchronousActionsToFinish($this->getSession());
59
        }
60
    }
61
62
    public function addToCartWithOption(ProductOptionInterface $option, string $optionValue): void
63
    {
64
        $select = $this->getElement('option_select', ['%optionCode%' => $option->getCode()]);
65
66
        $this->getDocument()->selectFieldOption($select->getAttribute('name'), $optionValue);
67
        $this->getElement('add_to_cart_button')->click();
68
    }
69
70
    public function getAttributeByName(string $name): ?string
71
    {
72
        $attributesTable = $this->getElement('attributes');
73
74
        $driver = $this->getDriver();
75
        if ($driver instanceof Selenium2Driver) {
76
            try {
77
                $attributesTab = $this->getElement('tab', ['%name%' => 'attributes']);
78
                if (!$attributesTab->hasAttribute('[data-test-active]')) {
79
                    $attributesTab->click();
80
                }
81
            } catch (ElementNotFoundException $exception) {
82
                return null;
83
            }
84
        }
85
86
        $nameTd = $attributesTable->find('css', sprintf('[data-test-product-attribute-name="%s"]', $name));
87
88
        if (null === $nameTd) {
89
            return null;
90
        }
91
92
        $row = $nameTd->getParent();
93
94
        return trim($row->find('css', 'td.sylius-product-attribute-value')->getText());
95
    }
96
97
    public function getAttributes(): array
98
    {
99
        $attributesTable = $this->getElement('attributes');
100
101
        return $attributesTable->findAll('css', 'tr > td[data-test-product-attribute-name]');
102
    }
103
104
    public function getAverageRating(): float
105
    {
106
        return (float) $this->getElement('average_rating')->getAttribute('data-test-average-rating');
107
    }
108
109
    public function getCurrentUrl(): string
110
    {
111
        return $this->getDriver()->getCurrentUrl();
112
    }
113
114
    public function getCurrentVariantName(): string
115
    {
116
        $currentVariantRow = $this->getElement('current_variant_input')->getParent()->getParent();
117
118
        return $currentVariantRow->find('css', 'td:first-child')->getText();
119
    }
120
121
    public function getName(): string
122
    {
123
        return $this->getElement('product_name')->getText();
124
    }
125
126
    public function getPrice(): string
127
    {
128
        return $this->getElement('product_price')->getText();
129
    }
130
131
    public function hasAddToCartButton(): bool
132
    {
133
        if (!$this->hasElement('add_to_cart_button')) {
134
            return false;
135
        }
136
137
        return $this->getElement('add_to_cart_button') !== null && false === $this->getElement('add_to_cart_button')->hasAttribute('disabled');
138
    }
139
140
    public function hasAssociation(string $productAssociationName): bool
141
    {
142
        try{
143
            $this->getElement('association', ['%associationName%' => $productAssociationName]);
144
        } catch (ElementNotFoundException $e) {
145
            return false;
146
        }
147
148
        return true;
149
    }
150
151
    public function hasProductInAssociation(string $productName, string $productAssociationName): bool
152
    {
153
        $products = $this->getElement('association', ['%associationName%' => $productAssociationName]);
154
155
        Assert::notNull($products);
156
157
        return $productName === $products->find('css', sprintf('[data-test-product-name="%s"]', $productName))->getText();
158
    }
159
160
    public function hasProductOutOfStockValidationMessage(ProductInterface $product): bool
161
    {
162
        $message = sprintf('%s does not have sufficient stock.', $product->getName());
163
164
        if (!$this->hasElement('validation_errors')) {
165
            return false;
166
        }
167
168
        return $this->getElement('validation_errors')->getText() === $message;
169
    }
170
171
    public function hasReviewTitled(string $title): bool
172
    {
173
        try{
174
            $element = $this->getElement('reviews_comment', ['%title%' => $title]);
175
        } catch (ElementNotFoundException $e) {
176
            return false;
177
        }
178
179
        return $title === $element->getAttribute('data-test-comment');
180
    }
181
182
    public function isOutOfStock(): bool
183
    {
184
        return $this->hasElement('out_of_stock');
185
    }
186
187
    public function isMainImageDisplayed(): bool
188
    {
189
        if ( !$this->hasElement('main_image')) {
190
            return false;
191
        }
192
193
        $imageUrl = $this->getElement('main_image')->getAttribute('src');
194
        $this->getDriver()->visit($imageUrl);
195
        $pageText = $this->getDocument()->getText();
196
        $this->getDriver()->back();
197
198
        return false === stripos($pageText, '404 Not Found');
199
    }
200
201
    public function countReviews(): int
202
    {
203
        return count($this->getElement('reviews')->findAll('css', '[data-test-comment]'));
204
    }
205
206
    public function selectOption(string $optionCode, string $optionValue): void
207
    {
208
        $optionElement = $this->getElement('option_select', ['%optionCode%' => strtoupper($optionCode)]);
209
        $optionElement->selectOption($optionValue);
210
    }
211
212
    public function selectVariant(string $variantName): void
213
    {
214
        $variantRadio = $this->getElement('variant_radio', ['%variantName%' => $variantName]);
215
216
        $driver = $this->getDriver();
217
        if ($driver instanceof Selenium2Driver) {
218
            $variantRadio->click();
219
220
            return;
221
        }
222
223
        $this->getDocument()->fillField($variantRadio->getAttribute('name'), $variantRadio->getAttribute('value'));
224
    }
225
226
    public function visit($url): void
227
    {
228
        $absoluteUrl = $this->makePathAbsolute($url);
229
        $this->getDriver()->visit($absoluteUrl);
230
    }
231
232
    public function open(array $urlParameters = []): void
233
    {
234
        $start = microtime(true);
235
        $end = $start + 5;
236
        do {
237
            try {
238
                parent::open($urlParameters);
239
                $isOpen = true;
240
            } catch (UnexpectedPageException $exception) {
241
                $isOpen = false;
242
                sleep(1);
243
            }
244
        } while (!$isOpen && microtime(true) < $end);
245
246
        if (!$isOpen) {
247
            throw new UnexpectedPageException();
248
        }
249
    }
250
251
    protected function getDefinedElements(): array
252
    {
253
        return array_merge(parent::getDefinedElements(), [
254
            'add_to_cart_button' => '[data-test-add-to-cart-button]',
255
            'association' => '[data-test-product-association="%associationName%"]',
256
            'attributes' => '[data-test-product-attributes]',
257
            'average_rating' => '[data-test-average-rating]',
258
            'current_variant_input' => '[data-test-product-variants] td input:checked',
259
            'main_image' => '[data-test-main-image]',
260
            'name' => '[data-test-product-name]',
261
            'option_select' => '#sylius_add_to_cart_cartItem_variant_%optionCode%',
262
            'out_of_stock' => '[data-test-product-out-of-stock]',
263
            'product_price' => '[data-test-product-price]',
264
            'product_name' => '[data-test-product-name]',
265
            'reviews' => '[data-test-product-reviews]',
266
            'reviews_comment' => '[data-test-comment="%title%"]',
267
            'selecting_variants' => '[data-test-product-selecting-variant]',
268
            'tab' => '[data-test-tab="%name%"]',
269
            'quantity' => '[data-test-quantity]',
270
            'validation_errors' => '[data-test-cart-validation-error]',
271
            'variant_radio' => '[data-test-product-variants] tbody tr:contains("%variantName%") input',
272
        ]);
273
    }
274
}
275