Completed
Push — remove-content-bundle ( 201341...8d07b3 )
by Kamil
52:29 queued 32:39
created

UpdateSimpleProductPage   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 384
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
wmc 40
c 0
b 0
f 0
lcom 1
cbo 9
dl 0
loc 384
rs 8.2608

31 Methods

Rating   Name   Duplication   Size   Complexity  
A specifyPrice() 0 4 1
A nameItIn() 0 8 1
A getAttributeValue() 0 9 2
A hasAttribute() 0 4 1
A selectMainTaxon() 0 15 1
A isMainTaxonChosen() 0 6 1
A disableTracking() 0 4 1
A enableTracking() 0 4 1
A isTracked() 0 4 1
A isImageWithCodeDisplayed() 0 15 2
A attachImage() 0 15 2
A changeImageWithCode() 0 7 1
A removeImageWithCode() 0 7 1
A removeFirstImage() 0 5 1
A countImages() 0 6 1
A isImageCodeDisabled() 0 4 1
A isSlugReadOnly() 0 4 1
A getValidationMessageForImage() 0 13 2
B associateProducts() 0 25 2
A hasAssociatedProduct() 0 9 1
A removeAssociatedProduct() 0 13 1
A getCodeElement() 0 4 1
A getDefinedElements() 0 19 1
A openTaxonBookmarks() 0 4 1
A clickTabIfItsNotActive() 0 7 2
A clickTab() 0 5 1
A getImageElementByCode() 0 11 2
A getImageElements() 0 6 1
A getLastImageElement() 0 8 1
A getFirstImageElement() 0 8 1
A waitForSlugGenerationIfNecessary() 0 16 3

How to fix   Complexity   

Complex Class

Complex classes like UpdateSimpleProductPage 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 UpdateSimpleProductPage, 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
namespace Sylius\Behat\Page\Admin\Product;
13
14
use Behat\Mink\Driver\Selenium2Driver;
15
use Behat\Mink\Element\NodeElement;
16
use Behat\Mink\Exception\ElementNotFoundException;
17
use Sylius\Behat\Behaviour\ChecksCodeImmutability;
18
use Sylius\Behat\Page\Admin\Crud\UpdatePage as BaseUpdatePage;
19
use Sylius\Component\Core\Model\TaxonInterface;
20
use Sylius\Component\Product\Model\ProductAssociationTypeInterface;
21
use Webmozart\Assert\Assert;
22
23
/**
24
 * @author Łukasz Chruściel <[email protected]>
25
 */
26
class UpdateSimpleProductPage extends BaseUpdatePage implements UpdateSimpleProductPageInterface
27
{
28
    use ChecksCodeImmutability;
29
30
    /**
31
     * {@inheritdoc}
32
     */
33
    public function nameItIn($name, $localeCode)
34
    {
35
        $this->getDocument()->fillField(
36
            sprintf('sylius_product_translations_%s_name', $localeCode), $name
37
        );
38
39
        $this->waitForSlugGenerationIfNecessary();
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45
    public function specifyPrice($price)
46
    {
47
        $this->getDocument()->fillField('Price', $price);
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53
    public function getAttributeValue($attribute)
54
    {
55
        $attributesTab = $this->getElement('tab', ['%name%' => 'attributes']);
56
        if (!$attributesTab->hasClass('active')) {
57
            $attributesTab->click();
58
        }
59
60
        return $this->getElement('attribute', ['%attribute%' => $attribute])->getValue();
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     */
66
    public function hasAttribute($attribute)
67
    {
68
        return null !== $this->getDocument()->find('css', sprintf('.attribute .label:contains("%s")', $attribute));
69
    }
70
71
    /**
72
     * {@inheritdoc}
73
     */
74
    public function selectMainTaxon(TaxonInterface $taxon)
75
    {
76
        $this->openTaxonBookmarks();
77
78
        Assert::isInstanceOf($this->getDriver(), Selenium2Driver::class);
79
80
        $this->getDriver()->executeScript(sprintf('$(\'input.search\').val(\'%s\')', $taxon->getName()));
81
        $this->getElement('search')->click();
82
        $this->getElement('search')->waitFor(10,
83
            function () {
84
                return $this->hasElement('search_item_selected');
85
            });
86
        $itemSelected = $this->getElement('search_item_selected');
87
        $itemSelected->click();
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93
    public function isMainTaxonChosen($taxonName)
94
    {
95
        $this->openTaxonBookmarks();
96
97
        return $taxonName === $this->getDocument()->find('css', '.search > .text')->getText();
98
    }
99
100
    public function disableTracking()
101
    {
102
        $this->getElement('tracked')->uncheck();
103
    }
104
105
    public function enableTracking()
106
    {
107
        $this->getElement('tracked')->check();
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113
    public function isTracked()
114
    {
115
        return $this->getElement('tracked')->isChecked();
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121
    public function isImageWithCodeDisplayed($code)
122
    {
123
        $imageElement = $this->getImageElementByCode($code);
124
125
        if (null === $imageElement) {
126
            return false;
127
        }
128
129
        $imageUrl = $imageElement->find('css', 'img')->getAttribute('src');
130
        $this->getDriver()->visit($imageUrl);
131
        $pageText = $this->getDocument()->getText();
132
        $this->getDriver()->back();
133
134
        return false === stripos($pageText, '404 Not Found');
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140
    public function attachImage($path, $code = null)
141
    {
142
        $this->clickTabIfItsNotActive('media');
143
144
        $filesPath = $this->getParameter('files_path');
145
146
        $this->getDocument()->clickLink('Add');
147
148
        $imageForm = $this->getLastImageElement();
149
        if (null !== $code) {
150
            $imageForm->fillField('Code', $code);
151
        }
152
153
        $imageForm->find('css', 'input[type="file"]')->attachFile($filesPath.$path);
154
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159
    public function changeImageWithCode($code, $path)
160
    {
161
        $filesPath = $this->getParameter('files_path');
162
163
        $imageForm = $this->getImageElementByCode($code);
164
        $imageForm->find('css', 'input[type="file"]')->attachFile($filesPath.$path);
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
170
    public function removeImageWithCode($code)
171
    {
172
        $this->clickTabIfItsNotActive('media');
173
174
        $imageElement = $this->getImageElementByCode($code);
175
        $imageElement->clickLink('Delete');
176
    }
177
178
    public function removeFirstImage()
179
    {
180
        $imageElement = $this->getFirstImageElement();
181
        $imageElement->clickLink('Delete');
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187
    public function countImages()
188
    {
189
        $imageElements = $this->getImageElements();
190
191
        return count($imageElements);
192
    }
193
194
    /**
195
     * {@inheritdoc}
196
     */
197
    public function isImageCodeDisabled()
198
    {
199
        return 'disabled' === $this->getLastImageElement()->findField('Code')->getAttribute('disabled');
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205
    public function isSlugReadOnly()
206
    {
207
        return 'readonly' === $this->getElement('slug')->getAttribute('readonly');
208
    }
209
210
    /**
211
     * {@inheritdoc}
212
     */
213
    public function getValidationMessageForImage()
214
    {
215
        $this->clickTabIfItsNotActive('media');
216
217
        $imageForm = $this->getLastImageElement();
218
219
        $foundElement = $imageForm->find('css', '.sylius-validation-error');
220
        if (null === $foundElement) {
221
            throw new ElementNotFoundException($this->getSession(), 'Tag', 'css', '.sylius-validation-error');
222
        }
223
224
        return $foundElement->getText();
225
    }
226
227
    /**
228
     * {@inheritdoc}
229
     */
230
    public function associateProducts(ProductAssociationTypeInterface $productAssociationType, array $productsNames)
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $productAssociationType exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
231
    {
232
        $this->clickTab('associations');
233
234
        Assert::isInstanceOf($this->getDriver(), Selenium2Driver::class);
235
236
        $dropdown = $this->getElement('association_dropdown', [
237
            '%association%' => $productAssociationType->getName()
238
        ]);
239
        $dropdown->click();
240
        $dropdown->waitFor(10, function () use ($productsNames, $productAssociationType) {
241
            return $this->hasElement('association_dropdown_item', [
242
                '%association%' => $productAssociationType->getName(),
243
                '%item%' => $productsNames[0],
244
            ]);
245
        });
246
247
        foreach ($productsNames as $productName) {
248
            $item = $this->getElement('association_dropdown_item', [
249
                '%association%' => $productAssociationType->getName(),
250
                '%item%' => $productName,
251
            ]);
252
            $item->click();
253
        }
254
    }
255
256
    /**
257
     * {@inheritdoc}
258
     */
259
    public function hasAssociatedProduct($productName, ProductAssociationTypeInterface $productAssociationType)
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $productAssociationType exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
260
    {
261
        $this->clickTabIfItsNotActive('associations');
262
263
        return $this->hasElement('association_dropdown_item', [
264
            '%association%' => $productAssociationType->getName(),
265
            '%item%' => $productName,
266
        ]);
267
    }
268
269
    /**
270
     * {@inheritdoc}
271
     */
272
    public function removeAssociatedProduct($productName, ProductAssociationTypeInterface $productAssociationType)
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $productAssociationType exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
273
    {
274
        $this->clickTabIfItsNotActive('associations');
275
276
        $item = $this->getElement('association_dropdown_item_selected', [
277
            '%association%' => $productAssociationType->getName(),
278
            '%item%' => $productName,
279
        ]);
280
281
        $deleteIcon = $item->find('css', 'i.delete');
282
        Assert::notNull($deleteIcon);
283
        $deleteIcon->click();
284
    }
285
286
    /**
287
     * {@inheritdoc}
288
     */
289
    protected function getCodeElement()
290
    {
291
        return $this->getElement('code');
292
    }
293
294
    /**
295
     * {@inheritdoc}
296
     */
297
    protected function getDefinedElements()
298
    {
299
        return array_merge(parent::getDefinedElements(), [
300
            'association_dropdown' => '.field > label:contains("%association%") ~ .product-select',
301
            'association_dropdown_item' => '.field > label:contains("%association%") ~ .product-select > div.menu > div.item:contains("%item%")',
302
            'association_dropdown_item_selected' => '.field > label:contains("%association%") ~ .product-select > a.label:contains("%item%")',
303
            'attribute' => '.attribute .label:contains("%attribute%") ~ input',
304
            'code' => '#sylius_product_code',
305
            'images' => '#sylius_product_images',
306
            'name' => '#sylius_product_translations_en_US_name',
307
            'price' => '#sylius_product_variant_price',
308
            'search' => '.ui.fluid.search.selection.dropdown',
309
            'search_item_selected' => 'div.menu > div.item.selected',
310
            'slug' => '#sylius_product_translations_en_US_slug',
311
            'tab' => '.menu [data-tab="%name%"]',
312
            'taxonomy' => 'a[data-tab="taxonomy"]',
313
            'tracked' => '#sylius_product_variant_tracked',
314
        ]);
315
    }
316
317
    private function openTaxonBookmarks()
318
    {
319
        $this->getElement('taxonomy')->click();
320
    }
321
322
    /**
323
     * @param string $tabName
324
     */
325
    private function clickTabIfItsNotActive($tabName)
326
    {
327
        $attributesTab = $this->getElement('tab', ['%name%' => $tabName]);
328
        if (!$attributesTab->hasClass('active')) {
329
            $attributesTab->click();
330
        }
331
    }
332
333
    /**
334
     * @param string $tabName
335
     */
336
    private function clickTab($tabName)
337
    {
338
        $attributesTab = $this->getElement('tab', ['%name%' => $tabName]);
339
        $attributesTab->click();
340
    }
341
342
    /**
343
     * @param string $code
344
     *
345
     * @return NodeElement
346
     */
347
    private function getImageElementByCode($code)
348
    {
349
        $images = $this->getElement('images');
350
        $inputCode = $images->find('css', 'input[value="'.$code.'"]');
351
352
        if (null === $inputCode) {
353
            return null;
354
        }
355
356
        return $inputCode->getParent()->getParent()->getParent();
357
    }
358
359
    /**
360
     * @return NodeElement[]
361
     */
362
    private function getImageElements()
363
    {
364
        $images = $this->getElement('images');
365
366
        return $images->findAll('css', 'div[data-form-collection="item"]');
367
    }
368
369
    /**
370
     * @return NodeElement
371
     */
372
    private function getLastImageElement()
373
    {
374
        $imageElements = $this->getImageElements();
375
376
        Assert::notEmpty($imageElements);
377
378
        return end($imageElements);
379
    }
380
381
    /**
382
     * @return NodeElement
383
     */
384
    private function getFirstImageElement()
385
    {
386
        $imageElements = $this->getImageElements();
387
388
        Assert::notEmpty($imageElements);
389
390
        return reset($imageElements);
391
    }
392
393
    private function waitForSlugGenerationIfNecessary()
394
    {
395
        if (!$this->getDriver() instanceof Selenium2Driver) {
396
            return;
397
        }
398
399
        $slugElement = $this->getElement('slug');
400
        if ($slugElement->hasAttribute('readonly')) {
401
            return;
402
        }
403
404
        $value = $slugElement->getValue();
405
        $this->getDocument()->waitFor(10, function () use ($slugElement, $value) {
406
            return $value !== $slugElement->getValue();
407
        });
408
    }
409
}
410