Completed
Push — unused-definitions ( d9908f )
by Kamil
18:33
created

theProductForCurrencyAndChannelShouldBePricedAt()   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 3
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\Page\SymfonyPageInterface;
27
use Sylius\Behat\Service\NotificationCheckerInterface;
28
use Sylius\Behat\Service\Resolver\CurrentPageResolverInterface;
29
use Sylius\Component\Core\Model\ChannelInterface;
30
use Sylius\Component\Core\Model\ProductInterface;
31
use Sylius\Behat\Service\SharedStorageInterface;
32
use Sylius\Component\Currency\Model\CurrencyInterface;
33
use Sylius\Component\Product\Model\ProductAssociationTypeInterface;
34
use Sylius\Component\Taxonomy\Model\TaxonInterface;
35
use Webmozart\Assert\Assert;
36
37
/**
38
 * @author Kamil Kokot <[email protected]>
39
 * @author Magdalena Banasiak <[email protected]>
40
 * @author Łukasz Chruściel <[email protected]>
41
 */
42
final class ManagingProductsContext implements Context
43
{
44
    /**
45
     * @var SharedStorageInterface
46
     */
47
    private $sharedStorage;
48
49
    /**x
50
     * @var CreateSimpleProductPageInterface
51
     */
52
    private $createSimpleProductPage;
53
54
    /**
55
     * @var CreateConfigurableProductPageInterface
56
     */
57
    private $createConfigurableProductPage;
58
59
    /**
60
     * @var IndexPageInterface
61
     */
62
    private $indexPage;
63
64
    /**
65
     * @var UpdateSimpleProductPageInterface
66
     */
67
    private $updateSimpleProductPage;
68
69
    /**
70
     * @var UpdateConfigurableProductPageInterface
71
     */
72
    private $updateConfigurableProductPage;
73
74
    /**
75
     * @var ProductReviewIndexPageInterface
76
     */
77
    private $productReviewIndexPage;
78
79
    /**
80
     * @var IndexPerTaxonPageInterface
81
     */
82
    private $indexPerTaxonPage;
83
84
    /**
85
     * @var CurrentPageResolverInterface
86
     */
87
    private $currentPageResolver;
88
89
    /**
90
     * @var NotificationCheckerInterface
91
     */
92
    private $notificationChecker;
93
94
    /**
95
     * @param SharedStorageInterface $sharedStorage
96
     * @param CreateSimpleProductPageInterface $createSimpleProductPage
97
     * @param CreateConfigurableProductPageInterface $createConfigurableProductPage
98
     * @param IndexPageInterface $indexPage
99
     * @param UpdateSimpleProductPageInterface $updateSimpleProductPage
100
     * @param UpdateConfigurableProductPageInterface $updateConfigurableProductPage
101
     * @param ProductReviewIndexPageInterface $productReviewIndexPage
102
     * @param IndexPerTaxonPageInterface $indexPerTaxonPage
103
     * @param CurrentPageResolverInterface $currentPageResolver
104
     * @param NotificationCheckerInterface $notificationChecker
105
     */
106
    public function __construct(
107
        SharedStorageInterface $sharedStorage,
108
        CreateSimpleProductPageInterface $createSimpleProductPage,
109
        CreateConfigurableProductPageInterface $createConfigurableProductPage,
110
        IndexPageInterface $indexPage,
111
        UpdateSimpleProductPageInterface $updateSimpleProductPage,
112
        UpdateConfigurableProductPageInterface $updateConfigurableProductPage,
113
        ProductReviewIndexPageInterface $productReviewIndexPage,
114
        IndexPerTaxonPageInterface $indexPerTaxonPage,
115
        CurrentPageResolverInterface $currentPageResolver,
116
        NotificationCheckerInterface $notificationChecker
117
    ) {
118
        $this->sharedStorage = $sharedStorage;
119
        $this->createSimpleProductPage = $createSimpleProductPage;
120
        $this->createConfigurableProductPage = $createConfigurableProductPage;
121
        $this->indexPage = $indexPage;
122
        $this->updateSimpleProductPage = $updateSimpleProductPage;
123
        $this->updateConfigurableProductPage = $updateConfigurableProductPage;
124
        $this->productReviewIndexPage = $productReviewIndexPage;
125
        $this->indexPerTaxonPage = $indexPerTaxonPage;
126
        $this->currentPageResolver = $currentPageResolver;
127
        $this->notificationChecker = $notificationChecker;
128
    }
129
130
    /**
131
     * @Given I want to create a new simple product
132
     */
133
    public function iWantToCreateANewSimpleProduct()
134
    {
135
        $this->createSimpleProductPage->open();
136
    }
137
138
    /**
139
     * @Given I want to create a new configurable product
140
     */
141
    public function iWantToCreateANewConfigurableProduct()
142
    {
143
        $this->createConfigurableProductPage->open();
144
    }
145
146
    /**
147
     * @When I specify its code as :code
148
     * @When I do not specify its code
149
     */
150
    public function iSpecifyItsCodeAs($code = null)
151
    {
152
        $currentPage = $this->resolveCurrentPage();
153
154
        $currentPage->specifyCode($code);
155
    }
156
157
    /**
158
     * @When I name it :name in :language
159
     * @When I rename it to :name in :language
160
     */
161
    public function iRenameItToIn($name, $language)
162
    {
163
        $currentPage = $this->resolveCurrentPage();
164
165
        $currentPage->nameItIn($name, $language);
166
    }
167
168
    /**
169
     * @When I add it
170
     * @When I try to add it
171
     */
172
    public function iAddIt()
173
    {
174
        /** @var CreatePageInterface $currentPage */
175
        $currentPage = $this->resolveCurrentPage();
176
177
        $currentPage->create();
178
    }
179
180
    /**
181
     * @When I disable its inventory tracking
182
     */
183
    public function iDisableItsTracking()
184
    {
185
        $this->updateSimpleProductPage->disableTracking();
186
    }
187
188
    /**
189
     * @When I enable its inventory tracking
190
     */
191
    public function iEnableItsTracking()
192
    {
193
        $this->updateSimpleProductPage->enableTracking();
194
    }
195
196
    /**
197
     * @When /^I set its(?:| default) price to "(?:€|£|\$)([^"]+)" for "([^"]+)" channel$/
198
     */
199
    public function iSetItsPriceTo($price, $channelName)
200
    {
201
        $this->createSimpleProductPage->specifyPrice($channelName, $price);
202
    }
203
204
    /**
205
     * @When I make it available in channel :channel
206
     */
207
    public function iMakeItAvailableInChannel($channel)
208
    {
209
        $this->createSimpleProductPage->checkChannel($channel);
210
    }
211
212
    /**
213
     * @When I choose :calculatorName calculator
214
     */
215
    public function iChooseCalculator($calculatorName)
216
    {
217
        $this->createSimpleProductPage->choosePricingCalculator($calculatorName);
218
    }
219
220
    /**
221
     * @When I set its slug to :slug
222
     * @When I set its slug to :slug in :language
223
     * @When I remove its slug
224
     */
225
    public function iSetItsSlugToIn($slug = null, $language = 'en_US')
226
    {
227
        $this->createSimpleProductPage->specifySlugIn($slug, $language);
228
    }
229
230
    /**
231
     * @When I enable slug modification
232
     * @When I enable slug modification in :localeCode
233
     */
234
    public function iEnableSlugModification($localeCode = 'en_US')
235
    {
236
        $this->updateSimpleProductPage->activateLanguageTab($localeCode);
237
        $this->updateSimpleProductPage->enableSlugModification($localeCode);
238
    }
239
240
    /**
241
     * @Then the product :productName should appear in the store
242
     * @Then the product :productName should be in the shop
243
     * @Then this product should still be named :productName
244
     */
245
    public function theProductShouldAppearInTheShop($productName)
246
    {
247
        $this->iWantToBrowseProducts();
248
249
        Assert::true(
250
            $this->indexPage->isSingleResourceOnPage(['name' => $productName]),
251
            sprintf('The product with name %s has not been found.', $productName)
252
        );
253
    }
254
255
    /**
256
     * @Given I am browsing products
257
     * @When I want to browse products
258
     */
259
    public function iWantToBrowseProducts()
260
    {
261
        $this->indexPage->open();
262
    }
263
264
    /**
265
     * @When /^I am browsing products from ("([^"]+)" taxon)$/
266
     */
267
    public function iAmBrowsingProductsFromTaxon(TaxonInterface $taxon)
268
    {
269
        $this->indexPerTaxonPage->open(['taxonId' => $taxon->getId()]);
270
    }
271
272
    /**
273
     * @When I filter them by :taxonName taxon
274
     */
275
    public function iFilterThemByTaxon($taxonName)
276
    {
277
        $this->indexPage->filterByTaxon($taxonName);
278
    }
279
280
    /**
281
     * @Then I should( still) see a product with :field :value
282
     */
283
    public function iShouldSeeProductWith($field, $value)
284
    {
285
        Assert::true(
286
            $this->indexPage->isSingleResourceOnPage([$field => $value]),
287
            sprintf('The product with %s "%s" has not been found.', $field, $value)
288
        );
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(
297
            $this->indexPage->isSingleResourceOnPage([$field => $value]),
298
            sprintf('The product with %s "%s" has been found.', $field, $value)
299
        );
300
    }
301
302
    /**
303
     * @Then the first product on the list should have :field :value
304
     */
305
    public function theFirstProductOnTheListShouldHave($field, $value)
306
    {
307
        $currentPage = $this->resolveCurrentPage();
308
309
        $actualValue = $currentPage->getColumnFields($field)[0];
310
311
        Assert::same(
312
            $actualValue,
313
            $value,
314
            sprintf('Expected first product\'s %s to be "%s", but it is "%s".', $field, $value, $actualValue)
315
        );
316
    }
317
318
    /**
319
     * @Then the last product on the list should have :field :value
320
     */
321
    public function theLastProductOnTheListShouldHave($field, $value)
322
    {
323
        $columnFields = $this->indexPerTaxonPage->getColumnFields($field);
324
        $actualValue = end($columnFields);
325
326
        Assert::same(
327
            $actualValue,
328
            $value,
329
            sprintf('Expected last product\'s %s to be "%s", but it is "%s".', $field, $value, $actualValue)
330
        );
331
    }
332
333
    /**
334
     * @When I switch the way products are sorted by :field
335
     * @When I start sorting products by :field
336
     * @Given the products are already sorted by :field
337
     */
338
    public function iSortProductsBy($field)
339
    {
340
        $this->indexPage->sortBy($field);
341
    }
342
343
    /**
344
     * @Then I should see :numberOfProducts products in the list
345
     */
346
    public function iShouldSeeProductsInTheList($numberOfProducts)
347
    {
348
        $foundRows = $this->indexPage->countItems();
349
350
        Assert::same(
351
            (int) $numberOfProducts,
352
            $foundRows,
353
            '%s rows with products should appear on page, %s rows has been found'
354
        );
355
    }
356
357
    /**
358
     * @When I delete the :product product
359
     * @When I try to delete the :product product
360
     */
361
    public function iDeleteProduct(ProductInterface $product)
362
    {
363
        $this->sharedStorage->set('product', $product);
364
365
        $this->iWantToBrowseProducts();
366
        $this->indexPage->deleteResourceOnPage(['name' => $product->getName()]);
367
    }
368
369
    /**
370
     * @Then /^(this product) should not exist in the product catalog$/
371
     */
372
    public function productShouldNotExist(ProductInterface $product)
373
    {
374
        $this->iWantToBrowseProducts();
375
376
        Assert::false(
377
            $this->indexPage->isSingleResourceOnPage(['code' => $product->getCode()]),
378
            sprintf('Product with code %s exists but should not.', $product->getCode())
379
        );
380
    }
381
382
    /**
383
     * @Then I should be notified that this product is in use and cannot be deleted
384
     */
385
    public function iShouldBeNotifiedOfFailure()
386
    {
387
        $this->notificationChecker->checkNotification(
388
            "Cannot delete, the product is in use.",
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal Cannot delete, the product is in use. does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
389
            NotificationType::failure()
390
        );
391
    }
392
393
    /**
394
     * @Then /^(this product) should still exist in the product catalog$/
395
     */
396
    public function productShouldExistInTheProductCatalog(ProductInterface $product)
397
    {
398
        $this->theProductShouldAppearInTheShop($product->getName());
399
    }
400
401
    /**
402
     * @When I want to modify the :product product
403
     * @When /^I want to modify (this product)$/
404
     */
405
    public function iWantToModifyAProduct(ProductInterface $product)
406
    {
407
        $this->sharedStorage->set('product', $product);
408
409
        if ($product->isSimple()) {
410
            $this->updateSimpleProductPage->open(['id' => $product->getId()]);
411
            return;
412
        }
413
414
        $this->updateConfigurableProductPage->open(['id' => $product->getId()]);
415
    }
416
417
    /**
418
     * @Then the code field should be disabled
419
     */
420
    public function theCodeFieldShouldBeDisabled()
421
    {
422
        $currentPage = $this->resolveCurrentPage();
423
424
        Assert::true(
425
            $currentPage->isCodeDisabled(),
426
            'Code should be immutable, but it does not.'
427
        );
428
    }
429
430
    /**
431
     * @Then the slug field should not be editable
432
     * @Then the slug field in :localeCode (also )should not be editable
433
     */
434
    public function theSlugFieldShouldNotBeEditable($localeCode = 'en_US')
435
    {
436
        Assert::true(
437
            $this->updateSimpleProductPage->isSlugReadOnlyIn($localeCode),
438
            'Slug should be immutable, but it does not.'
439
        );
440
    }
441
442
    /**
443
     * @Then this product name should be :name
444
     */
445
    public function thisProductElementShouldBe($name)
446
    {
447
        $this->assertElementValue('name', $name);
448
    }
449
450
    /**
451
     * @Then /^I should be notified that (code|name|slug) is required$/
452
     */
453
    public function iShouldBeNotifiedThatIsRequired($element)
454
    {
455
        $this->assertValidationMessage($element, sprintf('Please enter product %s.', $element));
456
    }
457
458
    /**
459
     * @When I save my changes
460
     * @When I try to save my changes
461
     */
462
    public function iSaveMyChanges()
463
    {
464
        /** @var UpdatePageInterface $currentPage */
465
        $currentPage = $this->resolveCurrentPage();
466
467
        $currentPage->saveChanges();
468
    }
469
470
    /**
471
     * @When /^I change its price to (?:€|£|\$)([^"]+) for "([^"]+)" channel$/
472
     */
473
    public function iChangeItsPriceTo($price, $channelName)
474
    {
475
        $this->updateSimpleProductPage->specifyPrice($channelName, $price);
476
    }
477
478
    /**
479
     * @Given I add the :optionName option to it
480
     */
481
    public function iAddTheOptionToIt($optionName)
482
    {
483
        $this->createConfigurableProductPage->selectOption($optionName);
484
    }
485
486
    /**
487
     * @When I set its :attribute attribute to :value
488
     */
489
    public function iSetItsAttributeTo($attribute, $value)
490
    {
491
        $this->createSimpleProductPage->addAttribute($attribute, $value);
492
    }
493
494
    /**
495
     * @When I remove its :attribute attribute
496
     */
497
    public function iRemoveItsAttribute($attribute)
498
    {
499
        $this->createSimpleProductPage->removeAttribute($attribute);
500
    }
501
502
    /**
503
     * @When I try to add new attributes
504
     */
505
    public function iTryToAddNewAttributes()
506
    {
507
        $this->updateSimpleProductPage->addSelectedAttributes();
508
    }
509
510
    /**
511
     * @Then /^attribute "([^"]+)" of (product "[^"]+") should be "([^"]+)"$/
512
     */
513
    public function itsAttributeShouldBe($attribute, ProductInterface $product, $value)
514
    {
515
        $this->updateSimpleProductPage->open(['id' => $product->getId()]);
516
517
        Assert::same(
518
            $value,
519
            $this->updateSimpleProductPage->getAttributeValue($attribute),
520
            sprintf('ProductAttribute "%s" should have value "%s" but it does not.', $attribute, $value)
521
        );
522
    }
523
524
    /**
525
     * @Then /^(product "[^"]+") should not have a "([^"]+)" attribute$/
526
     */
527
    public function productShouldNotHaveAttribute(ProductInterface $product, $attribute)
528
    {
529
        $this->updateSimpleProductPage->open(['id' => $product->getId()]);
530
531
        Assert::false(
532
            $this->updateSimpleProductPage->hasAttribute($attribute),
533
            sprintf('Product "%s" should not have attribute "%s" but it does.', $product->getName(), $attribute)
534
        );
535
    }
536
537
    /**
538
     * @Then product :product should not have any attributes
539
     * @Then product :product should have :count attribute
540
     */
541
    public function productShouldNotHaveAnyAttributes(ProductInterface $product, $count = 0)
542
    {
543
        $numberOfAttributes = $this->updateSimpleProductPage->getNumberOfAttributes();
544
545
        Assert::same(
546
            (int) $count,
547
            $numberOfAttributes,
548
            sprintf('Product "%s" should have %d attributes, but it has %d.', $product->getName(), $count, $numberOfAttributes)
549
        );
550
    }
551
552
    /**
553
     * @Given product with :element :value should not be added
554
     */
555
    public function productWithNameShouldNotBeAdded($element, $value)
556
    {
557
        $this->iWantToBrowseProducts();
558
559
        Assert::false(
560
            $this->indexPage->isSingleResourceOnPage([$element => $value]),
561
            sprintf('Product with %s %s was created, but it should not.', $element, $value)
562
        );
563
    }
564
565
    /**
566
     * @When I remove its name from :language translation
567
     */
568
    public function iRemoveItsNameFromTranslation($language)
569
    {
570
        $currentPage = $this->resolveCurrentPage();
571
572
        $currentPage->nameItIn('', $language);
573
    }
574
575
    /**
576
     * @Then /^this product should have (?:a|an) "([^"]+)" option$/
577
     */
578
    public function thisProductShouldHaveOption($productOption)
579
    {
580
        $this->updateConfigurableProductPage->isProductOptionChosen($productOption);
581
    }
582
583
    /**
584
     * @Then the option field should be disabled
585
     */
586
    public function theOptionFieldShouldBeDisabled()
587
    {
588
        Assert::true(
589
            $this->updateConfigurableProductPage->isProductOptionsDisabled(),
590
            'Options field should be immutable, but it does not.'
591
        );
592
    }
593
594
    /**
595
     * @When /^I choose main (taxon "([^"]+)")$/
596
     */
597
    public function iChooseMainTaxon(TaxonInterface $taxon)
598
    {
599
        $currentPage = $this->resolveCurrentPage();
600
601
        $currentPage->selectMainTaxon($taxon);
602
    }
603
604
    /**
605
     * @Then /^the slug of the ("[^"]+" product) should(?:| still) be "([^"]+)"$/
606
     * @Then /^the slug of the ("[^"]+" product) should(?:| still) be "([^"]+)" (in the "[^"]+" locale)$/
607
     */
608
    public function productSlugShouldBe(ProductInterface $product, $slug, $locale = "en_US")
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal en_US does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
609
    {
610
        $this->updateSimpleProductPage->open(['id' => $product->getId()]);
611
612
        Assert::same(
613
            $this->updateSimpleProductPage->getSlug($locale),
614
            $slug,
615
            'Expected slug %2$s, but found %s.'
616
        );
617
    }
618
619
    /**
620
     * @Then /^(this product) main taxon should be "([^"]+)"$/
621
     */
622
    public function thisProductMainTaxonShouldBe(ProductInterface $product, $taxonName)
623
    {
624
        /** @var UpdatePageInterface $currentPage */
625
        $currentPage = $this->resolveCurrentPage();
626
627
        $currentPage->open(['id' => $product->getId()]);
628
629
        Assert::true(
630
            $this->updateConfigurableProductPage->isMainTaxonChosen($taxonName),
631
            sprintf('The main taxon %s should be chosen, but it does not.', $taxonName)
632
        );
633
    }
634
635
    /**
636
     * @Then /^inventory of (this product) should not be tracked$/
637
     */
638
    public function thisProductShouldNotBeTracked(ProductInterface $product)
639
    {
640
        $this->iWantToModifyAProduct($product);
641
642
        Assert::false(
643
            $this->updateSimpleProductPage->isTracked(),
644
            '"%s" should not be tracked, but it is.'
645
        );
646
    }
647
648
    /**
649
     * @Then /^inventory of (this product) should be tracked$/
650
     */
651
    public function thisProductShouldBeTracked(ProductInterface $product)
652
    {
653
        $this->iWantToModifyAProduct($product);
654
655
        Assert::true(
656
            $this->updateSimpleProductPage->isTracked(),
657
            '"%s" should be tracked, but it is not.'
658
        );
659
    }
660
661
    /**
662
     * @When I attach the :path image with a code :code
663
     */
664
    public function iAttachImageWithACode($path, $code)
665
    {
666
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
667
        $currentPage = $this->resolveCurrentPage();
668
669
        $currentPage->attachImage($path, $code);
670
    }
671
672
    /**
673
     * @When I attach the :path image without a code
674
     */
675
    public function iAttachImageWithoutACode($path)
676
    {
677
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
678
        $currentPage = $this->resolveCurrentPage();
679
680
        $currentPage->attachImage($path);
681
    }
682
683
    /**
684
     * @When I associate as :productAssociationType the :productName product
685
     * @When I associate as :productAssociationType the :firstProductName and :secondProductName products
686
     */
687
    public function iAssociateProductsAsProductAssociation(
688
        ProductAssociationTypeInterface $productAssociationType,
689
        ...$productsNames
690
    ) {
691
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
692
        $currentPage = $this->resolveCurrentPage();
693
694
        $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...
695
    }
696
697
    /**
698
     * @When I remove an associated product :productName from :productAssociationType
699
     */
700
    public function iRemoveAnAssociatedProductFromProductAssociation(
701
        $productName,
702
        ProductAssociationTypeInterface $productAssociationType
703
    ) {
704
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
705
        $currentPage = $this->resolveCurrentPage();
706
707
        $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...
708
    }
709
710
    /**
711
     * @Then /^(this product) should have(?:| also) an image with a code "([^"]*)"$/
712
     * @Then /^the (product "[^"]+") should have(?:| also) an image with a code "([^"]*)"$/
713
     */
714
    public function thisProductShouldHaveAnImageWithCode(ProductInterface $product, $code)
715
    {
716
        $this->sharedStorage->set('product', $product);
717
718
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
719
        $currentPage = $this->resolveCurrentPage();
720
721
        Assert::true(
722
            $currentPage->isImageWithCodeDisplayed($code),
723
            sprintf('Image with a code %s should have been displayed.', $code)
724
        );
725
    }
726
727
    /**
728
     * @Then /^(this product) should not have(?:| also) an image with a code "([^"]*)"$/
729
     */
730
    public function thisProductShouldNotHaveAnImageWithCode(ProductInterface $product, $code)
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...
731
    {
732
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
733
        $currentPage = $this->resolveCurrentPage();
734
735
        Assert::false(
736
            $currentPage->isImageWithCodeDisplayed($code),
737
            sprintf('Image with a code %s should not have been displayed.', $code)
738
        );
739
    }
740
741
    /**
742
     * @When I change the image with the :code code to :path
743
     */
744
    public function iChangeItsImageToPathForTheCode($path, $code)
745
    {
746
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
747
        $currentPage = $this->resolveCurrentPage();
748
749
        $currentPage->changeImageWithCode($code, $path);
750
    }
751
752
    /**
753
     * @When /^I remove(?:| also) an image with a code "([^"]*)"$/
754
     */
755
    public function iRemoveAnImageWithACode($code)
756
    {
757
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
758
        $currentPage = $this->resolveCurrentPage();
759
760
        $currentPage->removeImageWithCode($code);
761
    }
762
763
    /**
764
     * @When I remove the first image
765
     */
766
    public function iRemoveTheFirstImage()
767
    {
768
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
769
        $currentPage = $this->resolveCurrentPage();
770
771
        $currentPage->removeFirstImage();
772
    }
773
774
    /**
775
     * @Then /^(this product) should not have any images$/
776
     */
777
    public function thisProductShouldNotHaveImages(ProductInterface $product)
778
    {
779
        $this->iWantToModifyAProduct($product);
780
781
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
782
        $currentPage = $this->resolveCurrentPage();
783
784
        Assert::same(
785
            0,
786
            $currentPage->countImages(),
787
            'This product has %2$s, but it should not have.'
788
        );
789
    }
790
791
    /**
792
     * @Then the image code field should be disabled
793
     */
794
    public function theImageCodeFieldShouldBeDisabled()
795
    {
796
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
797
        $currentPage = $this->resolveCurrentPage();
798
799
        Assert::true(
800
            $currentPage->isImageCodeDisabled(),
801
            'Image code field should be disabled but it is not.'
802
        );
803
    }
804
805
    /**
806
     * @Then I should be notified that the image with this code already exists
807
     */
808
    public function iShouldBeNotifiedThatTheImageWithThisCodeAlreadyExists()
809
    {
810
        Assert::same($this->updateSimpleProductPage->getValidationMessageForImage('code'), 'Image code must be unique within this product.');
0 ignored issues
show
Unused Code introduced by
The call to UpdateSimpleProductPageI...dationMessageForImage() has too many arguments starting with 'code'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
811
    }
812
813
    /**
814
     * @Then I should be notified that an image code is required
815
     */
816
    public function iShouldBeNotifiedThatAnImageCodeIsRequired()
817
    {
818
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
819
        $currentPage = $this->resolveCurrentPage();
820
821
        Assert::same(
822
            $currentPage->getValidationMessageForImage(),
823
            'Please enter an image code.'
824
        );
825
    }
826
827
    /**
828
     * @Then there should still be only one image in the :product product
829
     */
830
    public function thereShouldStillBeOnlyOneImageInThisTaxon(ProductInterface $product)
831
    {
832
        $this->iWantToModifyAProduct($product);
833
834
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
835
        $currentPage = $this->resolveCurrentPage();
836
837
        Assert::same(
838
            1,
839
            $currentPage->countImages(),
840
            'This product has %2$s images, but it should have only one.'
841
        );
842
    }
843
844
    /**
845
     * @Then /^there should be no reviews of (this product)$/
846
     */
847
    public function thereAreNoProductReviews(ProductInterface $product)
848
    {
849
        $this->productReviewIndexPage->open();
850
851
        Assert::false(
852
            $this->productReviewIndexPage->isSingleResourceOnPage(['reviewSubject' => $product->getName()]),
853
            sprintf('There should be no reviews of %s.', $product->getName())
854
        );
855
    }
856
857
    /**
858
     * @Then this product should( also) have an association :productAssociationType with product :productName
859
     * @Then this product should( also) have an association :productAssociationType with products :firstProductName and :secondProductName
860
     */
861
    public function theProductShouldHaveAnAssociationWithProducts(
862
        ProductAssociationTypeInterface $productAssociationType,
863
        ...$productsNames
864
    ) {
865
        foreach ($productsNames as $productName) {
866
            Assert::true(
867
                $this->updateSimpleProductPage->hasAssociatedProduct($productName, $productAssociationType),
868
                sprintf(
869
                    'This product should have an association %s with product %s, but it does not.',
870
                    $productAssociationType->getName(),
871
                    $productName
872
                )
873
            );
874
        }
875
    }
876
877
    /**
878
     * @Then this product should not have an association :productAssociationType with product :productName
879
     */
880
    public function theProductShouldNotHaveAnAssociationWithProducts(
881
        ProductAssociationTypeInterface $productAssociationType,
882
        $productName
883
    ) {
884
        Assert::false(
885
            $this->updateSimpleProductPage->hasAssociatedProduct($productName, $productAssociationType),
886
            sprintf(
887
                'This product should not have an association %s with product %s, but it does.',
888
                $productAssociationType->getName(),
889
                $productName
890
            )
891
        );
892
    }
893
894
    /**
895
     * @Then I should be notified that simple product code has to be unique
896
     */
897
    public function iShouldBeNotifiedThatSimpleProductCodeHasToBeUnique()
898
    {
899
        $this->assertValidationMessage('code', 'Simple product code must be unique among all products and product variants.');
900
    }
901
902
    /**
903
     * @Then I should not be notified that simple product code has to be unique
904
     */
905
    public function iShouldNotBeNotifiedThatSimpleProductCodeHasToBeUnique()
906
    {
907
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
908
        $currentPage = $this->resolveCurrentPage();
909
910
        try {
911
            $validationMessge = $currentPage->getValidationMessage('code');
912
913
            Assert::notSame($validationMessge, 'Simple product code must be unique among all products and product variants.');
914
        } catch (ElementNotFoundException $e) {
915
            // intentionally left blank, as it could not have any validation at all
916
        }
917
    }
918
919
    /**
920
     * @Then I should be notified that code has to be unique
921
     */
922
    public function iShouldBeNotifiedThatCodeHasToBeUnique()
923
    {
924
        $this->assertValidationMessage('code', 'Product code must be unique.');
925
    }
926
927
    /**
928
     * @Then I should be notified that price must be defined for every channel
929
     */
930
    public function iShouldBeNotifiedThatPriceMustBeDefinedForEveryChannel()
931
    {
932
        $this->assertValidationMessage('channel_pricings', 'You must define price for every channel.');
933
    }
934
935
    /**
936
     * @Then they should have order like :firstProductName, :secondProductName and :thirdProductName
937
     */
938
    public function theyShouldHaveOrderLikeAnd(...$productNames)
939
    {
940
        Assert::true(
941
            $this->indexPerTaxonPage->hasProductsInOrder($productNames),
942
            'The products have wrong order.'
943
        );
944
    }
945
946
    /**
947
     * @When I save my new configuration
948
     */
949
    public function iSaveMyNewConfiguration()
950
    {
951
        $this->indexPerTaxonPage->savePositions();
952
    }
953
954
    /**
955
     * @When I set the position of :productName to :position
956
     */
957
    public function iSetThePositionOfTo($productName, $position)
958
    {
959
        $this->indexPerTaxonPage->setPositionOfProduct($productName, (int) $position);
960
    }
961
962
    /**
963
     * @Then this product should( still) have slug :value in :language
964
     */
965
    public function thisProductElementShouldHaveSlugIn($slug, $language)
966
    {
967
        Assert::same(
968
            $this->updateSimpleProductPage->getSlug($language),
969
            $slug,
970
            'Expected slug %2$s, but found %s.'
971
        );
972
    }
973
974
    /**
975
     * @When I set its shipping category as :shippingCategoryName
976
     */
977
    public function iSetItsShippingCategoryAs($shippingCategoryName)
978
    {
979
        $this->createSimpleProductPage->selectShippingCategory($shippingCategoryName);
980
    }
981
982
    /**
983
     * @Then /^(it|this product) should be priced at (?:€|£|\$)([^"]+) for channel "([^"]+)"$/
984
     * @Then /^(product "[^"]+") should be priced at (?:€|£|\$)([^"]+) for channel "([^"]+)"$/
985
     */
986
    public function itShouldBePricedAtForChannel(ProductInterface $product, $price, $channelName)
987
    {
988
        $this->updateSimpleProductPage->open(['id' => $product->getId()]);
989
990
        Assert::same(
991
            $this->updateSimpleProductPage->getPriceForChannel($channelName),
992
            $price
993
        );
994
    }
995
996
    /**
997
     * @Then /^(this product) should no longer have price for channel "([^"]+)"$/
998
     */
999
    public function thisProductShouldNoLongerHavePriceForChannel(ProductInterface $product, $channelName)
1000
    {
1001
        $this->updateSimpleProductPage->open(['id' => $product->getId()]);
1002
1003
        try {
1004
            $this->updateSimpleProductPage->getPriceForChannel($channelName);
1005
        } catch (ElementNotFoundException $exception) {
1006
            return;
1007
        }
1008
1009
        throw new \Exception(
1010
            sprintf('Product "%s" should not have price defined for channel "%s".', $product->getName(), $channelName)
1011
        );
1012
    }
1013
1014
    /**
1015
     * @Then I should be notified that the :imageNumber image should have an unique code
1016
     */
1017
    public function iShouldBeNotifiedThatTheFirstImageShouldHaveAnUniqueCode($imageNumber)
1018
    {
1019
        Assert::same(
1020
            $this->updateSimpleProductPage->getValidationMessageForImageAtPosition((int) $imageNumber - 1),
1021
            'Image code must be unique within this product.'
1022
        );
1023
    }
1024
1025
    /**
1026
     * @param string $element
1027
     * @param string $value
1028
     */
1029
    private function assertElementValue($element, $value)
1030
    {
1031
        /** @var UpdatePageInterface $currentPage */
1032
        $currentPage = $this->resolveCurrentPage();
1033
1034
        Assert::isInstanceOf($currentPage, UpdatePageInterface::class);
1035
1036
        Assert::true(
1037
            $currentPage->hasResourceValues(
1038
                [$element => $value]
1039
            ),
1040
            sprintf('Product should have %s with %s value.', $element, $value)
1041
        );
1042
    }
1043
1044
    /**
1045
     * @param string $element
1046
     * @param string $message
1047
     */
1048
    private function assertValidationMessage($element, $message)
1049
    {
1050
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
1051
        $currentPage = $this->resolveCurrentPage();
1052
1053
        Assert::same($currentPage->getValidationMessage($element), $message);
1054
    }
1055
1056
    /**
1057
     * @return SymfonyPageInterface
1058
     */
1059
    private function resolveCurrentPage()
1060
    {
1061
        return $this->currentPageResolver->getCurrentPageWithForm([
1062
            $this->indexPage,
1063
            $this->indexPerTaxonPage,
1064
            $this->createSimpleProductPage,
1065
            $this->createConfigurableProductPage,
1066
            $this->updateSimpleProductPage,
1067
            $this->updateConfigurableProductPage,
1068
        ]);
1069
    }
1070
}
1071