Completed
Push — master ( 1d849e...3a997f )
by Paweł
09:48
created

UpdatePage::isSlugReadonly()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
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\Taxon;
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\Behat\Service\AutocompleteHelper;
20
use Sylius\Behat\Service\SlugGenerationHelper;
21
use Sylius\Component\Core\Model\TaxonInterface;
22
use Webmozart\Assert\Assert;
23
24
/**
25
 * @author Arkadiusz Krakowiak <[email protected]>
26
 */
27
class UpdatePage extends BaseUpdatePage implements UpdatePageInterface
28
{
29
    use ChecksCodeImmutability;
30
31
    /**
32
     * {@inheritdoc}
33
     */
34
    public function chooseParent(TaxonInterface $taxon)
35
    {
36
        AutocompleteHelper::chooseValue($this->getSession(), $this->getElement('parent')->getParent(), $taxon->getName());
0 ignored issues
show
Bug introduced by
It seems like $this->getElement('parent')->getParent() can be null; however, chooseValue() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
37
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42
    public function describeItAs($description, $languageCode)
43
    {
44
        $this->getDocument()->fillField(sprintf('sylius_taxon_translations_%s_description', $languageCode), $description);
45
    }
46
47
    /**
48
     * {@inheritdoc}
49
     */
50
    public function nameIt($name, $languageCode)
51
    {
52
        $this->activateLanguageTab($languageCode);
53
        $this->getDocument()->fillField(sprintf('sylius_taxon_translations_%s_name', $languageCode), $name);
54
55
        if ($this->getDriver() instanceof Selenium2Driver) {
56
            SlugGenerationHelper::waitForSlugGeneration(
57
                $this->getSession(),
58
                $this->getElement('slug', ['%language%' => $languageCode])
59
            );
60
        }
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     */
66
    public function specifySlug($slug, $languageCode)
67
    {
68
        $this->getDocument()->fillField(sprintf('sylius_taxon_translations_%s_slug', $languageCode), $slug);
69
    }
70
71
    /**
72
     * {@inheritdoc}
73
     */
74
    public function attachImage($path, $type = null)
75
    {
76
        $filesPath = $this->getParameter('files_path');
77
78
        $this->getDocument()->find('css', '[data-form-collection="add"]')->click();
79
80
        $imageForm = $this->getLastImageElement();
81
        if (null !== $type) {
82
            $imageForm->fillField('Type', $type);
83
        }
84
85
        $imageForm->find('css', 'input[type="file"]')->attachFile($filesPath.$path);
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91
    public function isImageWithTypeDisplayed($type)
92
    {
93
        $imageElement = $this->getImageElementByType($type);
94
95
        if (null === $imageElement) {
96
            return false;
97
        }
98
99
        $imageUrl = $imageElement->find('css', 'img')->getAttribute('src');
100
        $this->getDriver()->visit($imageUrl);
101
        $pageText = $this->getDocument()->getText();
102
        $this->getDriver()->back();
103
104
        return false === stripos($pageText, '404 Not Found');
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public function isSlugReadonly($languageCode = 'en_US')
111
    {
112
        return SlugGenerationHelper::isSlugReadonly(
113
            $this->getSession(),
114
            $this->getElement('slug', ['%language%' => $languageCode])
115
        );
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121
    public function removeImageWithType($type)
122
    {
123
        $imageElement = $this->getImageElementByType($type);
124
        $imageElement->clickLink('Delete');
125
    }
126
127
    public function removeFirstImage()
128
    {
129
        $imageElement = $this->getFirstImageElement();
130
        $imageElement->clickLink('Delete');
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136
    public function enableSlugModification($languageCode = 'en_US')
137
    {
138
        SlugGenerationHelper::enableSlugModification(
139
            $this->getSession(),
140
            $this->getElement('toggle_taxon_slug_modification_button', ['%locale%' => $languageCode])
141
        );
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147
    public function countImages()
148
    {
149
        $imageElements = $this->getImageElements();
150
151
        return count($imageElements);
152
    }
153
154
    /**
155
     * {@inheritdoc}
156
     */
157
    public function changeImageWithType($type, $path)
158
    {
159
        $filesPath = $this->getParameter('files_path');
160
161
        $imageForm = $this->getImageElementByType($type);
162
        $imageForm->find('css', 'input[type="file"]')->attachFile($filesPath.$path);
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168
    public function modifyFirstImageType($type)
169
    {
170
        $firstImage = $this->getFirstImageElement();
171
172
        $typeField = $firstImage->findField('Type');
173
        $typeField->setValue($type);
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179
    public function getParent()
180
    {
181
        return $this->getElement('parent')->getValue();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getElement('parent')->getValue(); (string|boolean|array) is incompatible with the return type declared by the interface Sylius\Behat\Page\Admin\...ageInterface::getParent of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187
    public function getSlug($languageCode = 'en_US')
188
    {
189
        return $this->getElement('slug', ['%language%' => $languageCode])->getValue();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getElement...uageCode))->getValue(); (string|boolean|array) is incompatible with the return type declared by the interface Sylius\Behat\Page\Admin\...ePageInterface::getSlug of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195
    public function getValidationMessageForImage()
196
    {
197
        $lastImageElement = $this->getLastImageElement();
198
199
        $foundElement = $lastImageElement->find('css', '.sylius-validation-error');
200
        if (null === $foundElement) {
201
            throw new ElementNotFoundException($this->getSession(), 'Tag', 'css', '.sylius-validation-error');
202
        }
203
204
        return $foundElement->getText();
205
    }
206
207
    /**
208
     * {@inheritdoc}
209
     */
210
    public function getValidationMessageForImageAtPlace($place)
211
    {
212
        $images = $this->getImageElements();
213
214
        $foundElement = $images[$place]->find('css', '.sylius-validation-error');
215
        if (null === $foundElement) {
216
            throw new ElementNotFoundException($this->getSession(), 'Tag', 'css', '.sylius-validation-error');
217
        }
218
219
        return $foundElement->getText();
220
    }
221
222
    /**
223
     * {@inheritdoc}
224
     */
225
    public function activateLanguageTab($locale)
226
    {
227
        if (!$this->getDriver() instanceof Selenium2Driver) {
228
            return;
229
        }
230
231
        $languageTabTitle = $this->getElement('language_tab', ['%locale%' => $locale]);
232
        if (!$languageTabTitle->hasClass('active')) {
233
            $languageTabTitle->click();
234
        }
235
236
        $this->getDocument()->waitFor(10, function () use ($languageTabTitle) {
237
            return $languageTabTitle->hasClass('active');
238
        });
239
    }
240
241
    /**
242
     * {@inheritdoc}
243
     */
244
    protected function getElement($name, array $parameters = [])
245
    {
246
        if (!isset($parameters['%language%'])) {
247
            $parameters['%language%'] = 'en_US';
248
        }
249
250
        return parent::getElement($name, $parameters);
251
    }
252
253
    /**
254
     * @return NodeElement
255
     */
256
    protected function getCodeElement()
257
    {
258
        return $this->getElement('code');
259
    }
260
261
    /**
262
     * {@inheritdoc}
263
     */
264
    protected function getDefinedElements()
265
    {
266
        return array_merge(parent::getDefinedElements(), [
267
            'code' => '#sylius_taxon_code',
268
            'description' => '#sylius_taxon_translations_en_US_description',
269
            'images' => '#sylius_taxon_images',
270
            'language_tab' => '[data-locale="%locale%"] .title',
271
            'name' => '#sylius_taxon_translations_en_US_name',
272
            'parent' => '#sylius_taxon_parent',
273
            'slug' => '#sylius_taxon_translations_%language%_slug',
274
            'toggle_taxon_slug_modification_button' => '[data-locale="%locale%"] .toggle-taxon-slug-modification',
275
        ]);
276
    }
277
278
    /**
279
     * @return NodeElement
280
     */
281
    private function getLastImageElement()
282
    {
283
        $imageElements = $this->getImageElements();
284
285
        Assert::notEmpty($imageElements);
286
287
        return end($imageElements);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression end($imageElements); of type Behat\Mink\Element\NodeElement|false adds false to the return on line 287 which is incompatible with the return type documented by Sylius\Behat\Page\Admin\...ge::getLastImageElement of type Behat\Mink\Element\NodeElement. It seems like you forgot to handle an error condition.
Loading history...
288
    }
289
290
    /**
291
     * @return NodeElement
292
     */
293
    private function getFirstImageElement()
294
    {
295
        $imageElements = $this->getImageElements();
296
297
        Assert::notEmpty($imageElements);
298
299
        return reset($imageElements);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression reset($imageElements); of type Behat\Mink\Element\NodeElement|false adds false to the return on line 299 which is incompatible with the return type documented by Sylius\Behat\Page\Admin\...e::getFirstImageElement of type Behat\Mink\Element\NodeElement. It seems like you forgot to handle an error condition.
Loading history...
300
    }
301
302
    /**
303
     * @return NodeElement[]
304
     */
305
    private function getImageElements()
306
    {
307
        $images = $this->getElement('images');
308
309
        return $images->findAll('css', 'div[data-form-collection="item"]');
310
    }
311
312
    /**
313
     * @param string $type
314
     *
315
     * @return NodeElement
316
     */
317
    private function getImageElementByType($type)
318
    {
319
        $images = $this->getElement('images');
320
        $typeInput = $images->find('css', 'input[value="'.$type.'"]');
321
322
        if (null === $typeInput) {
323
            return null;
324
        }
325
326
        return $typeInput->getParent()->getParent()->getParent();
327
    }
328
}
329