Completed
Push — travis-xenial ( 07fa5a...2a4231 )
by Kamil
06:17
created

CreateSimpleProductPage   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 304
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

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

28 Methods

Rating   Name   Duplication   Size   Complexity  
A getRouteName() 0 4 1
A nameItIn() 0 13 2
A specifySlugIn() 0 6 1
A specifyPrice() 0 4 1
A specifyOriginalPrice() 0 4 1
A addAttribute() 0 13 1
A getAttributeValidationErrors() 0 9 1
A removeAttribute() 0 6 1
A checkAttributeErrors() 0 5 1
A selectMainTaxon() 0 8 1
A isMainTaxonChosen() 0 6 1
A attachImage() 0 15 2
A associateProducts() 0 26 2
A removeAssociatedProduct() 0 10 1
A choosePricingCalculator() 0 4 1
A checkChannel() 0 4 1
A activateLanguageTab() 0 11 3
A selectShippingCategory() 0 4 1
A setShippingRequired() 0 10 2
A getElement() 0 8 2
A getDefinedElements() 0 30 1
A openTaxonBookmarks() 0 4 1
A selectElementFromAttributesDropdown() 0 9 1
A waitForFormElement() 0 7 1
A clickTabIfItsNotActive() 0 15 4
A clickTab() 0 13 3
A clickLocaleTabIfItsNotActive() 0 15 4
A getLastImageElement() 0 9 1

How to fix   Complexity   

Complex Class

Complex classes like CreateSimpleProductPage 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 CreateSimpleProductPage, 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\Admin\Product;
15
16
use Behat\Mink\Driver\Selenium2Driver;
17
use Behat\Mink\Element\NodeElement;
18
use Sylius\Behat\Behaviour\SpecifiesItsCode;
19
use Sylius\Behat\Page\Admin\Crud\CreatePage as BaseCreatePage;
20
use Sylius\Behat\Service\AutocompleteHelper;
21
use Sylius\Behat\Service\SlugGenerationHelper;
22
use Sylius\Component\Core\Model\TaxonInterface;
23
use Sylius\Component\Product\Model\ProductAssociationTypeInterface;
24
use WebDriver\Exception;
25
use Webmozart\Assert\Assert;
26
27
class CreateSimpleProductPage extends BaseCreatePage implements CreateSimpleProductPageInterface
0 ignored issues
show
Bug introduced by
There is one abstract method getDocument in this class; you could implement it, or declare this class as abstract.
Loading history...
28
{
29
    use SpecifiesItsCode;
30
31
    public function getRouteName(): string
32
    {
33
        return parent::getRouteName() . '_simple';
34
    }
35
36
    public function nameItIn(string $name, string $localeCode): void
37
    {
38
        $this->clickTabIfItsNotActive('details');
39
        $this->activateLanguageTab($localeCode);
40
        $this->getElement('name', ['%locale%' => $localeCode])->setValue($name);
41
42
        if ($this->getDriver() instanceof Selenium2Driver) {
43
            SlugGenerationHelper::waitForSlugGeneration(
44
                $this->getSession(),
45
                $this->getElement('slug', ['%locale%' => $localeCode])
46
            );
47
        }
48
    }
49
50
    public function specifySlugIn(?string $slug, string $locale): void
51
    {
52
        $this->activateLanguageTab($locale);
53
54
        $this->getElement('slug', ['%locale%' => $locale])->setValue($slug);
55
    }
56
57
    public function specifyPrice(string $channelName, string $price): void
58
    {
59
        $this->getElement('price', ['%channelName%' => $channelName])->setValue($price);
60
    }
61
62
    public function specifyOriginalPrice(string $channelName, int $originalPrice): void
63
    {
64
        $this->getElement('original_price', ['%channelName%' => $channelName])->setValue($originalPrice);
65
    }
66
67
    public function addAttribute(string $attributeName, string $value, string $localeCode): void
68
    {
69
        $this->clickTabIfItsNotActive('attributes');
70
        $this->clickLocaleTabIfItsNotActive($localeCode);
71
72
        $attributeOption = $this->getElement('attributes_choice')->find('css', sprintf('option:contains("%s")', $attributeName));
73
        $this->selectElementFromAttributesDropdown($attributeOption->getAttribute('value'));
74
75
        $this->getDocument()->pressButton('Add attributes');
76
        $this->waitForFormElement();
77
78
        $this->getElement('attribute_value', ['%attributeName%' => $attributeName, '%localeCode%' => $localeCode])->setValue($value);
79
    }
80
81
    public function getAttributeValidationErrors(string $attributeName, string $localeCode): string
82
    {
83
        $this->clickTabIfItsNotActive('attributes');
84
        $this->clickLocaleTabIfItsNotActive($localeCode);
85
86
        $validationError = $this->getElement('attribute')->find('css', '.sylius-validation-error');
87
88
        return $validationError->getText();
89
    }
90
91
    public function removeAttribute(string $attributeName, string $localeCode): void
92
    {
93
        $this->clickTabIfItsNotActive('attributes');
94
95
        $this->getElement('attribute_delete_button', ['%attributeName%' => $attributeName, '%localeCode%' => $localeCode])->press();
96
    }
97
98
    public function checkAttributeErrors($attributeName, $localeCode): void
99
    {
100
        $this->clickTabIfItsNotActive('attributes');
101
        $this->clickLocaleTabIfItsNotActive($localeCode);
102
    }
103
104
    public function selectMainTaxon(TaxonInterface $taxon): void
105
    {
106
        $this->openTaxonBookmarks();
107
108
        $mainTaxonElement = $this->getElement('main_taxon')->getParent();
109
110
        AutocompleteHelper::chooseValue($this->getSession(), $mainTaxonElement, $taxon->getName());
111
    }
112
113
    public function isMainTaxonChosen(string $taxonName): bool
114
    {
115
        $this->openTaxonBookmarks();
116
117
        return $taxonName === $this->getDocument()->find('css', '.search > .text')->getText();
118
    }
119
120
    public function attachImage(string $path, string $type = null): void
121
    {
122
        $this->clickTabIfItsNotActive('media');
123
124
        $filesPath = $this->getParameter('files_path');
125
126
        $this->getDocument()->clickLink('Add');
127
128
        $imageForm = $this->getLastImageElement();
129
        if (null !== $type) {
130
            $imageForm->fillField('Type', $type);
131
        }
132
133
        $imageForm->find('css', 'input[type="file"]')->attachFile($filesPath . $path);
134
    }
135
136
    public function associateProducts(ProductAssociationTypeInterface $productAssociationType, array $productsNames): void
137
    {
138
        $this->clickTab('associations');
139
140
        Assert::isInstanceOf($this->getDriver(), Selenium2Driver::class);
141
142
        $dropdown = $this->getElement('association_dropdown', [
143
            '%association%' => $productAssociationType->getName(),
144
        ]);
145
        $dropdown->click();
146
147
        foreach ($productsNames as $productName) {
148
            $dropdown->waitFor(5, function () use ($productName, $productAssociationType) {
149
                return $this->hasElement('association_dropdown_item', [
150
                    '%association%' => $productAssociationType->getName(),
151
                    '%item%' => $productName,
152
                ]);
153
            });
154
155
            $item = $this->getElement('association_dropdown_item', [
156
                '%association%' => $productAssociationType->getName(),
157
                '%item%' => $productName,
158
            ]);
159
            $item->click();
160
        }
161
    }
162
163
    public function removeAssociatedProduct(string $productName, ProductAssociationTypeInterface $productAssociationType): void
164
    {
165
        $this->clickTabIfItsNotActive('associations');
166
167
        $item = $this->getElement('association_dropdown_item_selected', [
168
            '%association%' => $productAssociationType->getName(),
169
            '%item%' => $productName,
170
        ]);
171
        $item->find('css', 'i.delete')->click();
172
    }
173
174
    public function choosePricingCalculator(string $name): void
175
    {
176
        $this->getElement('price_calculator')->selectOption($name);
177
    }
178
179
    public function checkChannel(string $channelName): void
180
    {
181
        $this->getElement('channel_checkbox', ['%channelName%' => $channelName])->check();
182
    }
183
184
    public function activateLanguageTab(string $locale): void
185
    {
186
        if (!$this->getDriver() instanceof Selenium2Driver) {
187
            return;
188
        }
189
190
        $languageTabTitle = $this->getElement('language_tab', ['%locale%' => $locale]);
191
        if (!$languageTabTitle->hasClass('active')) {
192
            $languageTabTitle->click();
193
        }
194
    }
195
196
    public function selectShippingCategory(string $shippingCategoryName): void
197
    {
198
        $this->getElement('shipping_category')->selectOption($shippingCategoryName);
199
    }
200
201
    public function setShippingRequired(bool $isShippingRequired): void
202
    {
203
        if ($isShippingRequired) {
204
            $this->getElement('shipping_required')->check();
205
206
            return;
207
        }
208
209
        $this->getElement('shipping_required')->uncheck();
210
    }
211
212
    protected function getElement(string $name, array $parameters = []): NodeElement
213
    {
214
        if (!isset($parameters['%locale%'])) {
215
            $parameters['%locale%'] = 'en_US';
216
        }
217
218
        return parent::getElement($name, $parameters);
219
    }
220
221
    protected function getDefinedElements(): array
222
    {
223
        return array_merge(parent::getDefinedElements(), [
224
            'association_dropdown' => '.field > label:contains("%association%") ~ .product-select',
225
            'association_dropdown_item' => '.field > label:contains("%association%") ~ .product-select > div.menu > div.item:contains("%item%")',
226
            'association_dropdown_item_selected' => '.field > label:contains("%association%") ~ .product-select > a.label:contains("%item%")',
227
            'attribute' => '.attribute',
228
            'attribute_delete_button' => '.tab[data-tab="%localeCode%"] .attribute .label:contains("%attributeName%") ~ button',
229
            'attribute_value' => '.tab[data-tab="%localeCode%"] .attribute .label:contains("%attributeName%") ~ input',
230
            'attributes_choice' => '#sylius_product_attribute_choice',
231
            'channel_checkbox' => '.checkbox:contains("%channelName%") input',
232
            'channel_pricings' => '#sylius_product_variant_channelPricings',
233
            'code' => '#sylius_product_code',
234
            'form' => 'form[name="sylius_product"]',
235
            'images' => '#sylius_product_images',
236
            'language_tab' => '[data-locale="%locale%"] .title',
237
            'locale_tab' => '#attributesContainer .menu [data-tab="%localeCode%"]',
238
            'main_taxon' => '#sylius_product_mainTaxon',
239
            'name' => '#sylius_product_translations_%locale%_name',
240
            'price' => '#sylius_product_variant_channelPricings > .field:contains("%channelName%") input[name$="[price]"]',
241
            'original_price' => '#sylius_product_variant_channelPricings > .field:contains("%channelName%") input[name$="[originalPrice]"]',
242
            'price_calculator' => '#sylius_product_variant_pricingCalculator',
243
            'shipping_category' => '#sylius_product_variant_shippingCategory',
244
            'shipping_required' => '#sylius_product_variant_shippingRequired',
245
            'slug' => '#sylius_product_translations_%locale%_slug',
246
            'tab' => '.menu [data-tab="%name%"]',
247
            'taxonomy' => 'a[data-tab="taxonomy"]',
248
            'toggle_slug_modification_button' => '.toggle-product-slug-modification',
249
        ]);
250
    }
251
252
    private function openTaxonBookmarks(): void
253
    {
254
        $this->getElement('taxonomy')->click();
255
    }
256
257
    private function selectElementFromAttributesDropdown(string $id): void
258
    {
259
        /** @var Selenium2Driver $driver */
260
        $driver = $this->getDriver();
261
        Assert::isInstanceOf($driver, Selenium2Driver::class);
262
263
        $driver->executeScript('$(\'#sylius_product_attribute_choice\').dropdown(\'show\');');
264
        $driver->executeScript(sprintf('$(\'#sylius_product_attribute_choice\').dropdown(\'set selected\', \'%s\');', $id));
265
    }
266
267
    private function waitForFormElement(int $timeout = 5): void
268
    {
269
        $form = $this->getElement('form');
270
        $this->getDocument()->waitFor($timeout, function () use ($form) {
271
            return false === strpos($form->getAttribute('class'), 'loading');
272
        });
273
    }
274
275
    private function clickTabIfItsNotActive(string $tabName): void
276
    {
277
        do {
278
            try {
279
                $attributesTab = $this->getElement('tab', ['%name%' => $tabName]);
280
                if (!$attributesTab->hasClass('active')) {
281
                    $attributesTab->click();
282
                }
283
            } catch (Exception $exception) {
0 ignored issues
show
Bug introduced by
The class WebDriver\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
284
                continue;
285
            }
286
287
            break;
288
        } while (true);
289
    }
290
291
    private function clickTab(string $tabName): void
292
    {
293
        do {
294
            try {
295
                $attributesTab = $this->getElement('tab', ['%name%' => $tabName]);
296
                $attributesTab->click();
297
            } catch (Exception $exception) {
0 ignored issues
show
Bug introduced by
The class WebDriver\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
298
                continue;
299
            }
300
301
            break;
302
        } while (true);
303
    }
304
305
    private function clickLocaleTabIfItsNotActive(string $localeCode): void
306
    {
307
        do {
308
            try {
309
                $localeTab = $this->getElement('locale_tab', ['%localeCode%' => $localeCode]);
310
                if (!$localeTab->hasClass('active')) {
311
                    $localeTab->click();
312
                }
313
            } catch (Exception $exception) {
0 ignored issues
show
Bug introduced by
The class WebDriver\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
314
                continue;
315
            }
316
317
            break;
318
        } while (true);
319
    }
320
321
    private function getLastImageElement(): NodeElement
322
    {
323
        $images = $this->getElement('images');
324
        $items = $images->findAll('css', 'div[data-form-collection="item"]');
325
326
        Assert::notEmpty($items);
327
328
        return end($items);
329
    }
330
}
331