Completed
Push — assert-overflow ( 0688b3...fbf83a )
by Kamil
66:00 queued 44:43
created

iShouldBeNotifiedThatPriceMustBeDefinedForEveryChannel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
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\Context\Ui\Admin;
13
14
use Behat\Behat\Context\Context;
15
use Behat\Mink\Exception\ElementNotFoundException;
16
use Sylius\Behat\NotificationType;
17
use Sylius\Behat\Page\Admin\Crud\CreatePageInterface;
18
use Sylius\Behat\Page\Admin\Crud\UpdatePageInterface;
19
use Sylius\Behat\Page\Admin\Product\CreateConfigurableProductPageInterface;
20
use Sylius\Behat\Page\Admin\Product\CreateSimpleProductPageInterface;
21
use Sylius\Behat\Page\Admin\Product\IndexPageInterface;
22
use Sylius\Behat\Page\Admin\Product\IndexPerTaxonPageInterface;
23
use Sylius\Behat\Page\Admin\Product\UpdateConfigurableProductPageInterface;
24
use Sylius\Behat\Page\Admin\Product\UpdateSimpleProductPageInterface;
25
use Sylius\Behat\Page\Admin\ProductReview\IndexPageInterface as ProductReviewIndexPageInterface;
26
use Sylius\Behat\Service\NotificationCheckerInterface;
27
use Sylius\Behat\Service\Resolver\CurrentPageResolverInterface;
28
use Sylius\Behat\Service\SharedStorageInterface;
29
use Sylius\Component\Core\Model\ProductInterface;
30
use Sylius\Component\Product\Model\ProductAssociationTypeInterface;
31
use Sylius\Component\Taxonomy\Model\TaxonInterface;
32
use Webmozart\Assert\Assert;
33
34
/**
35
 * @author Kamil Kokot <[email protected]>
36
 * @author Magdalena Banasiak <[email protected]>
37
 * @author Łukasz Chruściel <[email protected]>
38
 */
39
final class ManagingProductsContext implements Context
40
{
41
    /**
42
     * @var SharedStorageInterface
43
     */
44
    private $sharedStorage;
45
46
    /**x
47
     * @var CreateSimpleProductPageInterface
48
     */
49
    private $createSimpleProductPage;
50
51
    /**
52
     * @var CreateConfigurableProductPageInterface
53
     */
54
    private $createConfigurableProductPage;
55
56
    /**
57
     * @var IndexPageInterface
58
     */
59
    private $indexPage;
60
61
    /**
62
     * @var UpdateSimpleProductPageInterface
63
     */
64
    private $updateSimpleProductPage;
65
66
    /**
67
     * @var UpdateConfigurableProductPageInterface
68
     */
69
    private $updateConfigurableProductPage;
70
71
    /**
72
     * @var ProductReviewIndexPageInterface
73
     */
74
    private $productReviewIndexPage;
75
76
    /**
77
     * @var IndexPerTaxonPageInterface
78
     */
79
    private $indexPerTaxonPage;
80
81
    /**
82
     * @var CurrentPageResolverInterface
83
     */
84
    private $currentPageResolver;
85
86
    /**
87
     * @var NotificationCheckerInterface
88
     */
89
    private $notificationChecker;
90
91
    /**
92
     * @param SharedStorageInterface $sharedStorage
93
     * @param CreateSimpleProductPageInterface $createSimpleProductPage
94
     * @param CreateConfigurableProductPageInterface $createConfigurableProductPage
95
     * @param IndexPageInterface $indexPage
96
     * @param UpdateSimpleProductPageInterface $updateSimpleProductPage
97
     * @param UpdateConfigurableProductPageInterface $updateConfigurableProductPage
98
     * @param ProductReviewIndexPageInterface $productReviewIndexPage
99
     * @param IndexPerTaxonPageInterface $indexPerTaxonPage
100
     * @param CurrentPageResolverInterface $currentPageResolver
101
     * @param NotificationCheckerInterface $notificationChecker
102
     */
103
    public function __construct(
104
        SharedStorageInterface $sharedStorage,
105
        CreateSimpleProductPageInterface $createSimpleProductPage,
106
        CreateConfigurableProductPageInterface $createConfigurableProductPage,
107
        IndexPageInterface $indexPage,
108
        UpdateSimpleProductPageInterface $updateSimpleProductPage,
109
        UpdateConfigurableProductPageInterface $updateConfigurableProductPage,
110
        ProductReviewIndexPageInterface $productReviewIndexPage,
111
        IndexPerTaxonPageInterface $indexPerTaxonPage,
112
        CurrentPageResolverInterface $currentPageResolver,
113
        NotificationCheckerInterface $notificationChecker
114
    ) {
115
        $this->sharedStorage = $sharedStorage;
116
        $this->createSimpleProductPage = $createSimpleProductPage;
117
        $this->createConfigurableProductPage = $createConfigurableProductPage;
118
        $this->indexPage = $indexPage;
119
        $this->updateSimpleProductPage = $updateSimpleProductPage;
120
        $this->updateConfigurableProductPage = $updateConfigurableProductPage;
121
        $this->productReviewIndexPage = $productReviewIndexPage;
122
        $this->indexPerTaxonPage = $indexPerTaxonPage;
123
        $this->currentPageResolver = $currentPageResolver;
124
        $this->notificationChecker = $notificationChecker;
125
    }
126
127
    /**
128
     * @Given I want to create a new simple product
129
     */
130
    public function iWantToCreateANewSimpleProduct()
131
    {
132
        $this->createSimpleProductPage->open();
133
    }
134
135
    /**
136
     * @Given I want to create a new configurable product
137
     */
138
    public function iWantToCreateANewConfigurableProduct()
139
    {
140
        $this->createConfigurableProductPage->open();
141
    }
142
143
    /**
144
     * @When I specify its code as :code
145
     * @When I do not specify its code
146
     */
147
    public function iSpecifyItsCodeAs($code = null)
148
    {
149
        $currentPage = $this->resolveCurrentPage();
150
151
        $currentPage->specifyCode($code);
0 ignored issues
show
Bug introduced by
The method specifyCode does only exist in Sylius\Behat\Page\Admin\...pleProductPageInterface, but not in Sylius\Behat\Page\Admin\...pleProductPageInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
152
    }
153
154
    /**
155
     * @When I name it :name in :language
156
     * @When I rename it to :name in :language
157
     */
158
    public function iRenameItToIn($name, $language)
159
    {
160
        $currentPage = $this->resolveCurrentPage();
161
162
        $currentPage->nameItIn($name, $language);
0 ignored issues
show
Bug introduced by
The method nameItIn does only exist in Sylius\Behat\Page\Admin\...pleProductPageInterface, but not in Sylius\Behat\Page\Admin\...exPerTaxonPageInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
163
    }
164
165
    /**
166
     * @When I add it
167
     * @When I try to add it
168
     */
169
    public function iAddIt()
170
    {
171
        /** @var CreatePageInterface $currentPage */
172
        $currentPage = $this->resolveCurrentPage();
173
174
        $currentPage->create();
175
    }
176
177
    /**
178
     * @When I disable its inventory tracking
179
     */
180
    public function iDisableItsTracking()
181
    {
182
        $this->updateSimpleProductPage->disableTracking();
183
    }
184
185
    /**
186
     * @When I enable its inventory tracking
187
     */
188
    public function iEnableItsTracking()
189
    {
190
        $this->updateSimpleProductPage->enableTracking();
191
    }
192
193
    /**
194
     * @When /^I set its(?:| default) price to "(?:€|£|\$)([^"]+)" for "([^"]+)" channel$/
195
     */
196
    public function iSetItsPriceTo($price, $channelName)
197
    {
198
        $this->createSimpleProductPage->specifyPrice($channelName, $price);
199
    }
200
201
    /**
202
     * @When I make it available in channel :channel
203
     */
204
    public function iMakeItAvailableInChannel($channel)
205
    {
206
        $this->createSimpleProductPage->checkChannel($channel);
207
    }
208
209
    /**
210
     * @When I assign it to channel :channel
211
     */
212
    public function iAssignItToChannel($channel)
213
    {
214
        // Temporary solution until we will make current page resolver work with product pages
215
        $this->updateConfigurableProductPage->checkChannel($channel);
216
    }
217
218
    /**
219
     * @When I choose :calculatorName calculator
220
     */
221
    public function iChooseCalculator($calculatorName)
222
    {
223
        $this->createSimpleProductPage->choosePricingCalculator($calculatorName);
224
    }
225
226
    /**
227
     * @When I set its slug to :slug
228
     * @When I set its slug to :slug in :language
229
     * @When I remove its slug
230
     */
231
    public function iSetItsSlugToIn($slug = null, $language = 'en_US')
232
    {
233
        $this->createSimpleProductPage->specifySlugIn($slug, $language);
234
    }
235
236
    /**
237
     * @When I enable slug modification
238
     * @When I enable slug modification in :localeCode
239
     */
240
    public function iEnableSlugModification($localeCode = 'en_US')
241
    {
242
        $this->updateSimpleProductPage->activateLanguageTab($localeCode);
243
        $this->updateSimpleProductPage->enableSlugModification($localeCode);
244
    }
245
246
    /**
247
     * @Then the product :productName should appear in the store
248
     * @Then the product :productName should be in the shop
249
     * @Then this product should still be named :productName
250
     */
251
    public function theProductShouldAppearInTheShop($productName)
252
    {
253
        $this->iWantToBrowseProducts();
254
255
        Assert::true($this->indexPage->isSingleResourceOnPage(['name' => $productName]));
256
    }
257
258
    /**
259
     * @Given I am browsing products
260
     * @When I want to browse products
261
     */
262
    public function iWantToBrowseProducts()
263
    {
264
        $this->indexPage->open();
265
    }
266
267
    /**
268
     * @When /^I am browsing products from ("([^"]+)" taxon)$/
269
     */
270
    public function iAmBrowsingProductsFromTaxon(TaxonInterface $taxon)
271
    {
272
        $this->indexPerTaxonPage->open(['taxonId' => $taxon->getId()]);
273
    }
274
275
    /**
276
     * @When I filter them by :taxonName taxon
277
     */
278
    public function iFilterThemByTaxon($taxonName)
279
    {
280
        $this->indexPage->filterByTaxon($taxonName);
281
    }
282
283
    /**
284
     * @Then I should( still) see a product with :field :value
285
     */
286
    public function iShouldSeeProductWith($field, $value)
287
    {
288
        Assert::true($this->indexPage->isSingleResourceOnPage([$field => $value]));
289
    }
290
291
    /**
292
     * @Then I should not see any product with :field :value
293
     */
294
    public function iShouldNotSeeAnyProductWith($field, $value)
295
    {
296
        Assert::false($this->indexPage->isSingleResourceOnPage([$field => $value]));
297
    }
298
299
    /**
300
     * @Then the first product on the list should have :field :value
301
     */
302
    public function theFirstProductOnTheListShouldHave($field, $value)
303
    {
304
        $currentPage = $this->resolveCurrentPage();
305
306
        Assert::same($currentPage->getColumnFields($field)[0], $value);
0 ignored issues
show
Bug introduced by
The method getColumnFields does only exist in Sylius\Behat\Page\Admin\...exPerTaxonPageInterface, but not in Sylius\Behat\Page\Admin\...pleProductPageInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
307
    }
308
309
    /**
310
     * @Then the last product on the list should have :field :value
311
     */
312
    public function theLastProductOnTheListShouldHave($field, $value)
313
    {
314
        $values = $this->indexPerTaxonPage->getColumnFields($field);
315
316
        Assert::same(end($values), $value);
317
    }
318
319
    /**
320
     * @When I switch the way products are sorted by :field
321
     * @When I start sorting products by :field
322
     * @Given the products are already sorted by :field
323
     */
324
    public function iSortProductsBy($field)
325
    {
326
        $this->indexPage->sortBy($field);
327
    }
328
329
    /**
330
     * @Then I should see :numberOfProducts products in the list
331
     */
332
    public function iShouldSeeProductsInTheList($numberOfProducts)
333
    {
334
        Assert::same($this->indexPage->countItems(), (int) $numberOfProducts);
335
    }
336
337
    /**
338
     * @When I delete the :product product
339
     * @When I try to delete the :product product
340
     */
341
    public function iDeleteProduct(ProductInterface $product)
342
    {
343
        $this->sharedStorage->set('product', $product);
344
345
        $this->iWantToBrowseProducts();
346
        $this->indexPage->deleteResourceOnPage(['name' => $product->getName()]);
347
    }
348
349
    /**
350
     * @Then /^(this product) should not exist in the product catalog$/
351
     */
352
    public function productShouldNotExist(ProductInterface $product)
353
    {
354
        $this->iWantToBrowseProducts();
355
356
        Assert::false($this->indexPage->isSingleResourceOnPage(['code' => $product->getCode()]));
357
    }
358
359
    /**
360
     * @Then I should be notified that this product is in use and cannot be deleted
361
     */
362
    public function iShouldBeNotifiedOfFailure()
363
    {
364
        $this->notificationChecker->checkNotification(
365
            'Cannot delete, the product is in use.',
366
            NotificationType::failure()
367
        );
368
    }
369
370
    /**
371
     * @Then /^(this product) should still exist in the product catalog$/
372
     */
373
    public function productShouldExistInTheProductCatalog(ProductInterface $product)
374
    {
375
        $this->theProductShouldAppearInTheShop($product->getName());
376
    }
377
378
    /**
379
     * @When I want to modify the :product product
380
     * @When /^I want to modify (this product)$/
381
     */
382
    public function iWantToModifyAProduct(ProductInterface $product)
383
    {
384
        $this->sharedStorage->set('product', $product);
385
386
        if ($product->isSimple()) {
387
            $this->updateSimpleProductPage->open(['id' => $product->getId()]);
388
            return;
389
        }
390
391
        $this->updateConfigurableProductPage->open(['id' => $product->getId()]);
392
    }
393
394
    /**
395
     * @Then the code field should be disabled
396
     */
397
    public function theCodeFieldShouldBeDisabled()
398
    {
399
        $currentPage = $this->resolveCurrentPage();
400
401
        Assert::true($currentPage->isCodeDisabled());
0 ignored issues
show
Bug introduced by
The method isCodeDisabled does only exist in Sylius\Behat\Page\Admin\...pleProductPageInterface, but not in Sylius\Behat\Page\Admin\...exPerTaxonPageInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
402
    }
403
404
    /**
405
     * @Then the slug field should not be editable
406
     * @Then the slug field in :localeCode (also )should not be editable
407
     */
408
    public function theSlugFieldShouldNotBeEditable($localeCode = 'en_US')
409
    {
410
        Assert::true($this->updateSimpleProductPage->isSlugReadOnlyIn($localeCode));
411
    }
412
413
    /**
414
     * @Then this product name should be :name
415
     */
416
    public function thisProductElementShouldBe($name)
417
    {
418
        $this->assertElementValue('name', $name);
419
    }
420
421
    /**
422
     * @Then /^I should be notified that (code|name|slug) is required$/
423
     */
424
    public function iShouldBeNotifiedThatIsRequired($element)
425
    {
426
        $this->assertValidationMessage($element, sprintf('Please enter product %s.', $element));
427
    }
428
429
    /**
430
     * @When I save my changes
431
     * @When I try to save my changes
432
     */
433
    public function iSaveMyChanges()
434
    {
435
        /** @var UpdatePageInterface $currentPage */
436
        $currentPage = $this->resolveCurrentPage();
437
438
        $currentPage->saveChanges();
439
    }
440
441
    /**
442
     * @When /^I change its price to (?:€|£|\$)([^"]+) for "([^"]+)" channel$/
443
     */
444
    public function iChangeItsPriceTo($price, $channelName)
445
    {
446
        $this->updateSimpleProductPage->specifyPrice($channelName, $price);
447
    }
448
449
    /**
450
     * @Given I add the :optionName option to it
451
     */
452
    public function iAddTheOptionToIt($optionName)
453
    {
454
        $this->createConfigurableProductPage->selectOption($optionName);
455
    }
456
457
    /**
458
     * @When I set its :attribute attribute to :value
459
     * @When I set its :attribute attribute to :value in :language
460
     */
461
    public function iSetItsAttributeTo($attribute, $value, $language = 'en_US')
462
    {
463
        $this->createSimpleProductPage->addAttribute($attribute, $value, $language);
464
    }
465
466
    /**
467
     * @When I remove its :attribute attribute
468
     * @When I remove its :attribute attribute from :language
469
     */
470
    public function iRemoveItsAttribute($attribute, $language = 'en_US')
471
    {
472
        $this->createSimpleProductPage->removeAttribute($attribute, $language);
473
    }
474
475
    /**
476
     * @When I try to add new attributes
477
     */
478
    public function iTryToAddNewAttributes()
479
    {
480
        $this->updateSimpleProductPage->addSelectedAttributes();
481
    }
482
483
    /**
484
     * @Then attribute :attributeName of product :product should be :value
485
     * @Then attribute :attributeName of product :product should be :value in :language
486
     */
487
    public function itsAttributeShouldBe($attributeName, ProductInterface $product, $value, $language = 'en_US')
488
    {
489
        $this->updateSimpleProductPage->open(['id' => $product->getId()]);
490
491
        Assert::same($this->updateSimpleProductPage->getAttributeValue($attributeName, $language), $value);
492
    }
493
494
    /**
495
     * @Then /^(product "[^"]+") should not have a "([^"]+)" attribute$/
496
     */
497
    public function productShouldNotHaveAttribute(ProductInterface $product, $attribute)
498
    {
499
        $this->updateSimpleProductPage->open(['id' => $product->getId()]);
500
501
        Assert::false($this->updateSimpleProductPage->hasAttribute($attribute));
502
    }
503
504
    /**
505
     * @Then product :product should not have any attributes
506
     * @Then product :product should have :count attribute
507
     */
508
    public function productShouldNotHaveAnyAttributes(ProductInterface $product, $count = 0)
0 ignored issues
show
Unused Code introduced by
The parameter $product is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
509
    {
510
        Assert::same($this->updateSimpleProductPage->getNumberOfAttributes(), (int) $count);
511
    }
512
513
    /**
514
     * @Given product with :element :value should not be added
515
     */
516
    public function productWithNameShouldNotBeAdded($element, $value)
517
    {
518
        $this->iWantToBrowseProducts();
519
520
        Assert::false($this->indexPage->isSingleResourceOnPage([$element => $value]));
521
    }
522
523
    /**
524
     * @When I remove its name from :language translation
525
     */
526
    public function iRemoveItsNameFromTranslation($language)
527
    {
528
        $currentPage = $this->resolveCurrentPage();
529
530
        $currentPage->nameItIn('', $language);
0 ignored issues
show
Bug introduced by
The method nameItIn does only exist in Sylius\Behat\Page\Admin\...pleProductPageInterface, but not in Sylius\Behat\Page\Admin\...exPerTaxonPageInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
531
    }
532
533
    /**
534
     * @Then /^this product should have (?:a|an) "([^"]+)" option$/
535
     */
536
    public function thisProductShouldHaveOption($productOption)
537
    {
538
        $this->updateConfigurableProductPage->isProductOptionChosen($productOption);
539
    }
540
541
    /**
542
     * @Then the option field should be disabled
543
     */
544
    public function theOptionFieldShouldBeDisabled()
545
    {
546
        Assert::true($this->updateConfigurableProductPage->isProductOptionsDisabled());
547
    }
548
549
    /**
550
     * @When /^I choose main (taxon "([^"]+)")$/
551
     */
552
    public function iChooseMainTaxon(TaxonInterface $taxon)
553
    {
554
        $currentPage = $this->resolveCurrentPage();
555
556
        $currentPage->selectMainTaxon($taxon);
0 ignored issues
show
Compatibility introduced by
$taxon of type object<Sylius\Component\...y\Model\TaxonInterface> is not a sub-type of object<Sylius\Component\...e\Model\TaxonInterface>. It seems like you assume a child interface of the interface Sylius\Component\Taxonomy\Model\TaxonInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Bug introduced by
The method selectMainTaxon does only exist in Sylius\Behat\Page\Admin\...pleProductPageInterface, but not in Sylius\Behat\Page\Admin\...exPerTaxonPageInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
557
    }
558
559
    /**
560
     * @Then /^the slug of the ("[^"]+" product) should(?:| still) be "([^"]+)"$/
561
     * @Then /^the slug of the ("[^"]+" product) should(?:| still) be "([^"]+)" (in the "[^"]+" locale)$/
562
     */
563
    public function productSlugShouldBe(ProductInterface $product, $slug, $locale = 'en_US')
564
    {
565
        $this->updateSimpleProductPage->open(['id' => $product->getId()]);
566
567
        Assert::same($this->updateSimpleProductPage->getSlug($locale), $slug);
568
    }
569
570
    /**
571
     * @Then /^(this product) main taxon should be "([^"]+)"$/
572
     */
573
    public function thisProductMainTaxonShouldBe(ProductInterface $product, $taxonName)
574
    {
575
        $currentPage = $this->resolveCurrentPage();
576
        $currentPage->open(['id' => $product->getId()]);
577
578
        Assert::true($currentPage->isMainTaxonChosen($taxonName));
0 ignored issues
show
Bug introduced by
The method isMainTaxonChosen does only exist in Sylius\Behat\Page\Admin\...pleProductPageInterface, but not in Sylius\Behat\Page\Admin\...exPerTaxonPageInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
579
    }
580
581
    /**
582
     * @Then /^inventory of (this product) should not be tracked$/
583
     */
584
    public function thisProductShouldNotBeTracked(ProductInterface $product)
585
    {
586
        $this->iWantToModifyAProduct($product);
587
588
        Assert::false($this->updateSimpleProductPage->isTracked());
589
    }
590
591
    /**
592
     * @Then /^inventory of (this product) should be tracked$/
593
     */
594
    public function thisProductShouldBeTracked(ProductInterface $product)
595
    {
596
        $this->iWantToModifyAProduct($product);
597
598
        Assert::true($this->updateSimpleProductPage->isTracked());
599
    }
600
601
    /**
602
     * @When I attach the :path image with a code :code
603
     */
604
    public function iAttachImageWithACode($path, $code)
605
    {
606
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
607
        $currentPage = $this->resolveCurrentPage();
608
609
        $currentPage->attachImage($path, $code);
610
    }
611
612
    /**
613
     * @When I attach the :path image without a code
614
     */
615
    public function iAttachImageWithoutACode($path)
616
    {
617
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
618
        $currentPage = $this->resolveCurrentPage();
619
620
        $currentPage->attachImage($path);
621
    }
622
623
    /**
624
     * @When I associate as :productAssociationType the :productName product
625
     * @When I associate as :productAssociationType the :firstProductName and :secondProductName products
626
     */
627
    public function iAssociateProductsAsProductAssociation(
628
        ProductAssociationTypeInterface $productAssociationType,
629
        ...$productsNames
630
    ) {
631
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
632
        $currentPage = $this->resolveCurrentPage();
633
634
        $currentPage->associateProducts($productAssociationType, $productsNames);
0 ignored issues
show
Bug introduced by
The method associateProducts does only exist in Sylius\Behat\Page\Admin\...pleProductPageInterface, but not in Sylius\Behat\Page\Admin\...bleProductPageInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
635
    }
636
637
    /**
638
     * @When I remove an associated product :productName from :productAssociationType
639
     */
640
    public function iRemoveAnAssociatedProductFromProductAssociation(
641
        $productName,
642
        ProductAssociationTypeInterface $productAssociationType
643
    ) {
644
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
645
        $currentPage = $this->resolveCurrentPage();
646
647
        $currentPage->removeAssociatedProduct($productName, $productAssociationType);
0 ignored issues
show
Bug introduced by
The method removeAssociatedProduct does only exist in Sylius\Behat\Page\Admin\...pleProductPageInterface, but not in Sylius\Behat\Page\Admin\...bleProductPageInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
648
    }
649
650
    /**
651
     * @Then /^(this product) should have(?:| also) an image with a code "([^"]*)"$/
652
     * @Then /^the (product "[^"]+") should have(?:| also) an image with a code "([^"]*)"$/
653
     */
654
    public function thisProductShouldHaveAnImageWithCode(ProductInterface $product, $code)
655
    {
656
        $this->sharedStorage->set('product', $product);
657
658
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
659
        $currentPage = $this->resolveCurrentPage();
660
661
        Assert::true($currentPage->isImageWithCodeDisplayed($code));
662
    }
663
664
    /**
665
     * @Then /^this product should not have(?:| also) an image with a code "([^"]*)"$/
666
     */
667
    public function thisProductShouldNotHaveAnImageWithCode($code)
668
    {
669
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
670
        $currentPage = $this->resolveCurrentPage();
671
672
        Assert::false($currentPage->isImageWithCodeDisplayed($code));
673
    }
674
675
    /**
676
     * @When I change the image with the :code code to :path
677
     */
678
    public function iChangeItsImageToPathForTheCode($path, $code)
679
    {
680
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
681
        $currentPage = $this->resolveCurrentPage();
682
683
        $currentPage->changeImageWithCode($code, $path);
684
    }
685
686
    /**
687
     * @When /^I remove(?:| also) an image with a code "([^"]*)"$/
688
     */
689
    public function iRemoveAnImageWithACode($code)
690
    {
691
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
692
        $currentPage = $this->resolveCurrentPage();
693
694
        $currentPage->removeImageWithCode($code);
695
    }
696
697
    /**
698
     * @When I remove the first image
699
     */
700
    public function iRemoveTheFirstImage()
701
    {
702
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
703
        $currentPage = $this->resolveCurrentPage();
704
705
        $currentPage->removeFirstImage();
706
    }
707
708
    /**
709
     * @Then /^(this product) should not have any images$/
710
     */
711
    public function thisProductShouldNotHaveImages(ProductInterface $product)
712
    {
713
        $this->iWantToModifyAProduct($product);
714
715
        $currentPage = $this->resolveCurrentPage();
716
717
        Assert::same($currentPage->countImages(), 0);
0 ignored issues
show
Bug introduced by
The method countImages does only exist in Sylius\Behat\Page\Admin\...pleProductPageInterface, but not in Sylius\Behat\Page\Admin\...exPerTaxonPageInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
718
    }
719
720
    /**
721
     * @Then the image code field should be disabled
722
     */
723
    public function theImageCodeFieldShouldBeDisabled()
724
    {
725
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
726
        $currentPage = $this->resolveCurrentPage();
727
728
        Assert::true($currentPage->isImageCodeDisabled());
729
    }
730
731
    /**
732
     * @Then I should be notified that the image with this code already exists
733
     */
734
    public function iShouldBeNotifiedThatTheImageWithThisCodeAlreadyExists()
735
    {
736
        Assert::same($this->updateSimpleProductPage->getValidationMessageForImage(), 'Image code must be unique within this product.');
737
    }
738
739
    /**
740
     * @Then I should be notified that an image code is required
741
     */
742
    public function iShouldBeNotifiedThatAnImageCodeIsRequired()
743
    {
744
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
745
        $currentPage = $this->resolveCurrentPage();
746
747
        Assert::same($currentPage->getValidationMessageForImage(), 'Please enter an image code.');
748
    }
749
750
    /**
751
     * @Then there should still be only one image in the :product product
752
     */
753
    public function thereShouldStillBeOnlyOneImageInThisTaxon(ProductInterface $product)
754
    {
755
        $this->iWantToModifyAProduct($product);
756
757
        $currentPage = $this->resolveCurrentPage();
758
759
        Assert::same($currentPage->countImages(), 1);
0 ignored issues
show
Bug introduced by
The method countImages does only exist in Sylius\Behat\Page\Admin\...pleProductPageInterface, but not in Sylius\Behat\Page\Admin\...exPerTaxonPageInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
760
    }
761
762
    /**
763
     * @Then /^there should be no reviews of (this product)$/
764
     */
765
    public function thereAreNoProductReviews(ProductInterface $product)
766
    {
767
        $this->productReviewIndexPage->open();
768
769
        Assert::false($this->productReviewIndexPage->isSingleResourceOnPage(['reviewSubject' => $product->getName()]));
770
    }
771
772
    /**
773
     * @Then this product should( also) have an association :productAssociationType with product :productName
774
     * @Then this product should( also) have an association :productAssociationType with products :firstProductName and :secondProductName
775
     */
776
    public function theProductShouldHaveAnAssociationWithProducts(
777
        ProductAssociationTypeInterface $productAssociationType,
778
        ...$productsNames
779
    ) {
780
        foreach ($productsNames as $productName) {
781
            Assert::true(
782
                $this->updateSimpleProductPage->hasAssociatedProduct($productName, $productAssociationType),
783
                sprintf(
784
                    'This product should have an association %s with product %s.',
785
                    $productAssociationType->getName(),
786
                    $productName
787
                )
788
            );
789
        }
790
    }
791
792
    /**
793
     * @Then this product should not have an association :productAssociationType with product :productName
794
     */
795
    public function theProductShouldNotHaveAnAssociationWithProducts(
796
        ProductAssociationTypeInterface $productAssociationType,
797
        $productName
798
    ) {
799
        Assert::false($this->updateSimpleProductPage->hasAssociatedProduct($productName, $productAssociationType));
800
    }
801
802
    /**
803
     * @Then I should be notified that simple product code has to be unique
804
     */
805
    public function iShouldBeNotifiedThatSimpleProductCodeHasToBeUnique()
806
    {
807
        $this->assertValidationMessage('code', 'Simple product code must be unique among all products and product variants.');
808
    }
809
810
    /**
811
     * @Then I should not be notified that simple product code has to be unique
812
     */
813
    public function iShouldNotBeNotifiedThatSimpleProductCodeHasToBeUnique()
814
    {
815
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
816
        $currentPage = $this->resolveCurrentPage();
817
818
        try {
819
            $validationMessge = $currentPage->getValidationMessage('code');
820
821
            Assert::notSame($validationMessge, 'Simple product code must be unique among all products and product variants.');
822
        } catch (ElementNotFoundException $e) {
823
            // intentionally left blank, as it could not have any validation at all
824
        }
825
    }
826
827
    /**
828
     * @Then I should be notified that slug has to be unique
829
     */
830
    public function iShouldBeNotifiedThatSlugHasToBeUnique()
831
    {
832
        $this->assertValidationMessage('slug', 'Product slug must be unique.');
833
    }
834
835
    /**
836
     * @Then I should be notified that code has to be unique
837
     */
838
    public function iShouldBeNotifiedThatCodeHasToBeUnique()
839
    {
840
        $this->assertValidationMessage('code', 'Product code must be unique.');
841
    }
842
843
    /**
844
     * @Then I should be notified that price must be defined for every channel
845
     */
846
    public function iShouldBeNotifiedThatPriceMustBeDefinedForEveryChannel()
847
    {
848
        $this->assertValidationMessage('channel_pricings', 'You must define price for every channel.');
849
    }
850
851
    /**
852
     * @Then they should have order like :firstProductName, :secondProductName and :thirdProductName
853
     */
854
    public function theyShouldHaveOrderLikeAnd(...$productNames)
855
    {
856
        Assert::true($this->indexPerTaxonPage->hasProductsInOrder($productNames));
857
    }
858
859
    /**
860
     * @When I save my new configuration
861
     */
862
    public function iSaveMyNewConfiguration()
863
    {
864
        $this->indexPerTaxonPage->savePositions();
865
    }
866
867
    /**
868
     * @When I set the position of :productName to :position
869
     */
870
    public function iSetThePositionOfTo($productName, $position)
871
    {
872
        $this->indexPerTaxonPage->setPositionOfProduct($productName, (int) $position);
873
    }
874
875
    /**
876
     * @Then this product should( still) have slug :value in :language
877
     */
878
    public function thisProductElementShouldHaveSlugIn($slug, $language)
879
    {
880
        Assert::same($this->updateSimpleProductPage->getSlug($language), $slug);
881
    }
882
883
    /**
884
     * @When I set its shipping category as :shippingCategoryName
885
     */
886
    public function iSetItsShippingCategoryAs($shippingCategoryName)
887
    {
888
        $this->createSimpleProductPage->selectShippingCategory($shippingCategoryName);
889
    }
890
891
    /**
892
     * @Then /^(it|this product) should be priced at (?:€|£|\$)([^"]+) for channel "([^"]+)"$/
893
     * @Then /^(product "[^"]+") should be priced at (?:€|£|\$)([^"]+) for channel "([^"]+)"$/
894
     */
895
    public function itShouldBePricedAtForChannel(ProductInterface $product, $price, $channelName)
896
    {
897
        $this->updateSimpleProductPage->open(['id' => $product->getId()]);
898
899
        Assert::same($this->updateSimpleProductPage->getPriceForChannel($channelName), $price);
900
    }
901
902
    /**
903
     * @Then /^(this product) should no longer have price for channel "([^"]+)"$/
904
     */
905
    public function thisProductShouldNoLongerHavePriceForChannel(ProductInterface $product, $channelName)
906
    {
907
        $this->updateSimpleProductPage->open(['id' => $product->getId()]);
908
909
        try {
910
            $this->updateSimpleProductPage->getPriceForChannel($channelName);
911
        } catch (ElementNotFoundException $exception) {
912
            return;
913
        }
914
915
        throw new \Exception(
916
            sprintf('Product "%s" should not have price defined for channel "%s".', $product->getName(), $channelName)
917
        );
918
    }
919
920
    /**
921
     * @Then I should be notified that the :imageNumber image should have an unique code
922
     */
923
    public function iShouldBeNotifiedThatTheFirstImageShouldHaveAnUniqueCode($imageNumber)
924
    {
925
        Assert::same(
926
            $this->updateSimpleProductPage->getValidationMessageForImageAtPosition((int) $imageNumber - 1),
927
            'Image code must be unique within this product.'
928
        );
929
    }
930
931
    /**
932
     * @Then I should be notified that I have to define product variants' prices for newly assigned channels first
933
     */
934
    public function iShouldBeNotifiedThatIHaveToDefineProductVariantsPricesForNewlyAssignedChannelsFirst()
935
    {
936
        Assert::same(
937
            $this->updateConfigurableProductPage->getValidationMessage('channels'),
938
            'You have to define product variants\' prices for newly assigned channels first.'
939
        );
940
    }
941
942
    /**
943
     * @param string $element
944
     * @param string $value
945
     */
946
    private function assertElementValue($element, $value)
947
    {
948
        /** @var UpdatePageInterface $currentPage */
949
        $currentPage = $this->resolveCurrentPage();
950
951
        Assert::isInstanceOf($currentPage, UpdatePageInterface::class);
952
953
        Assert::true(
954
            $currentPage->hasResourceValues([$element => $value]),
955
            sprintf('Product should have %s with %s value.', $element, $value)
956
        );
957
    }
958
959
    /**
960
     * @param string $element
961
     * @param string $message
962
     */
963
    private function assertValidationMessage($element, $message)
964
    {
965
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
966
        $currentPage = $this->resolveCurrentPage();
967
968
        Assert::same($currentPage->getValidationMessage($element), $message);
969
    }
970
971
    /**
972
     * @return IndexPageInterface|IndexPerTaxonPageInterface|CreateSimpleProductPageInterface|CreateConfigurableProductPageInterface|UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface
973
     */
974
    private function resolveCurrentPage()
975
    {
976
        return $this->currentPageResolver->getCurrentPageWithForm([
977
            $this->indexPage,
978
            $this->indexPerTaxonPage,
979
            $this->createSimpleProductPage,
980
            $this->createConfigurableProductPage,
981
            $this->updateSimpleProductPage,
982
            $this->updateConfigurableProductPage,
983
        ]);
984
    }
985
}
986