Completed
Push — master ( 7c0075...645752 )
by Kamil
18:52
created

ManagingProductsContext::iRenameItToIn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 2
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 Sylius\Behat\NotificationType;
16
use Sylius\Behat\Page\Admin\Crud\CreatePageInterface;
17
use Sylius\Behat\Page\Admin\Crud\UpdatePageInterface;
18
use Sylius\Behat\Page\Admin\Product\CreateConfigurableProductPageInterface;
19
use Sylius\Behat\Page\Admin\Product\CreateSimpleProductPageInterface;
20
use Sylius\Behat\Page\Admin\Product\IndexPageInterface;
21
use Sylius\Behat\Page\Admin\Product\IndexPerTaxonPageInterface;
22
use Sylius\Behat\Page\Admin\Product\UpdateConfigurableProductPageInterface;
23
use Sylius\Behat\Page\Admin\Product\UpdateSimpleProductPageInterface;
24
use Sylius\Behat\Page\Admin\ProductReview\IndexPageInterface as ProductReviewIndexPageInterface;
25
use Sylius\Behat\Service\NotificationCheckerInterface;
26
use Sylius\Behat\Service\Resolver\CurrentProductPageResolverInterface;
27
use Sylius\Component\Core\Model\ChannelInterface;
28
use Sylius\Component\Core\Model\ProductInterface;
29
use Sylius\Behat\Service\SharedStorageInterface;
30
use Sylius\Component\Currency\Model\CurrencyInterface;
31
use Sylius\Component\Product\Model\ProductAssociationTypeInterface;
32
use Sylius\Component\Taxonomy\Model\TaxonInterface;
33
use Webmozart\Assert\Assert;
34
35
/**
36
 * @author Kamil Kokot <[email protected]>
37
 * @author Magdalena Banasiak <[email protected]>
38
 * @author Łukasz Chruściel <[email protected]>
39
 */
40
final class ManagingProductsContext implements Context
41
{
42
    /**
43
     * @var SharedStorageInterface
44
     */
45
    private $sharedStorage;
46
47
    /**x
48
     * @var CreateSimpleProductPageInterface
49
     */
50
    private $createSimpleProductPage;
51
52
    /**
53
     * @var CreateConfigurableProductPageInterface
54
     */
55
    private $createConfigurableProductPage;
56
57
    /**
58
     * @var IndexPageInterface
59
     */
60
    private $indexPage;
61
62
    /**
63
     * @var UpdateSimpleProductPageInterface
64
     */
65
    private $updateSimpleProductPage;
66
67
    /**
68
     * @var UpdateConfigurableProductPageInterface
69
     */
70
    private $updateConfigurableProductPage;
71
72
    /**
73
     * @var ProductReviewIndexPageInterface
74
     */
75
    private $productReviewIndexPage;
76
77
    /**
78
     * @var IndexPerTaxonPageInterface
79
     */
80
    private $indexPerTaxonPage;
81
82
    /**
83
     * @var CurrentProductPageResolverInterface
84
     */
85
    private $currentPageResolver;
86
87
    /**
88
     * @var NotificationCheckerInterface
89
     */
90
    private $notificationChecker;
91
92
    /**
93
     * @param SharedStorageInterface $sharedStorage
94
     * @param CreateSimpleProductPageInterface $createSimpleProductPage
95
     * @param CreateConfigurableProductPageInterface $createConfigurableProductPage
96
     * @param IndexPageInterface $indexPage
97
     * @param UpdateSimpleProductPageInterface $updateSimpleProductPage
98
     * @param UpdateConfigurableProductPageInterface $updateConfigurableProductPage
99
     * @param ProductReviewIndexPageInterface $productReviewIndexPage
100
     * @param IndexPerTaxonPageInterface $indexPerTaxonPage
101
     * @param CurrentProductPageResolverInterface $currentPageResolver
102
     * @param NotificationCheckerInterface $notificationChecker
103
     */
104
    public function __construct(
105
        SharedStorageInterface $sharedStorage,
106
        CreateSimpleProductPageInterface $createSimpleProductPage,
107
        CreateConfigurableProductPageInterface $createConfigurableProductPage,
108
        IndexPageInterface $indexPage,
109
        UpdateSimpleProductPageInterface $updateSimpleProductPage,
110
        UpdateConfigurableProductPageInterface $updateConfigurableProductPage,
111
        ProductReviewIndexPageInterface $productReviewIndexPage,
112
        IndexPerTaxonPageInterface $indexPerTaxonPage,
113
        CurrentProductPageResolverInterface $currentPageResolver,
114
        NotificationCheckerInterface $notificationChecker
115
    ) {
116
        $this->sharedStorage = $sharedStorage;
117
        $this->createSimpleProductPage = $createSimpleProductPage;
118
        $this->createConfigurableProductPage = $createConfigurableProductPage;
119
        $this->indexPage = $indexPage;
120
        $this->updateSimpleProductPage = $updateSimpleProductPage;
121
        $this->updateConfigurableProductPage = $updateConfigurableProductPage;
122
        $this->productReviewIndexPage = $productReviewIndexPage;
123
        $this->indexPerTaxonPage = $indexPerTaxonPage;
124
        $this->currentPageResolver = $currentPageResolver;
125
        $this->notificationChecker = $notificationChecker;
126
    }
127
128
    /**
129
     * @Given I want to create a new simple product
130
     */
131
    public function iWantToCreateANewSimpleProduct()
132
    {
133
        $this->createSimpleProductPage->open();
134
    }
135
136
    /**
137
     * @Given I want to create a new configurable product
138
     */
139
    public function iWantToCreateANewConfigurableProduct()
140
    {
141
        $this->createConfigurableProductPage->open();
142
    }
143
144
    /**
145
     * @When I specify its code as :code
146
     * @When I do not specify its code
147
     */
148
    public function iSpecifyItsCodeAs($code = null)
149
    {
150
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
151
            $this->createSimpleProductPage,
152
            $this->createConfigurableProductPage,
153
        ]);
154
155
        $currentPage->specifyCode($code);
156
    }
157
158
    /**
159
     * @When I name it :name in :language
160
     */
161
    public function iNameItIn($name, $language)
162
    {
163
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
164
            $this->createSimpleProductPage,
165
            $this->createConfigurableProductPage,
166
        ]);
167
168
        $currentPage->nameItIn($name, $language);
169
    }
170
171
    /**
172
     * @When I rename it to :name in :language
173
     */
174
    public function iRenameItToIn($name, $language)
175
    {
176
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
177
            $this->updateSimpleProductPage,
178
            $this->updateConfigurableProductPage,
179
        ], $this->sharedStorage->get('product'));
180
181
        $currentPage->nameItIn($name, $language);
182
    }
183
184
    /**
185
     * @When I add it
186
     * @When I try to add it
187
     */
188
    public function iAddIt()
189
    {
190
        /** @var CreatePageInterface $currentPage */
191
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
192
            $this->createSimpleProductPage,
193
            $this->createConfigurableProductPage,
194
        ]);
195
196
        Assert::isInstanceOf($currentPage, CreatePageInterface::class);
197
198
        $currentPage->create();
199
    }
200
201
    /**
202
     * @When I disable its inventory tracking
203
     */
204
    public function iDisableItsTracking()
205
    {
206
        $this->updateSimpleProductPage->disableTracking();
207
    }
208
209
    /**
210
     * @When I enable its inventory tracking
211
     */
212
    public function iEnableItsTracking()
213
    {
214
        $this->updateSimpleProductPage->enableTracking();
215
    }
216
217
    /**
218
     * @When /^I set its(?:| default) price to ("(?:€|£|\$)[^"]+")$/
219
     */
220
    public function iSetItsPriceTo($price)
221
    {
222
        $this->createSimpleProductPage->specifyPrice($price);
223
    }
224
225
    /**
226
     * @When I choose :calculatorName calculator
227
     */
228
    public function iChooseCalculator($calculatorName)
229
    {
230
        $this->createSimpleProductPage->choosePricingCalculator($calculatorName);
231
    }
232
233
    /**
234
     * @When /^I set its price to "(?:€|£|\$)([^"]+)" for ("[^"]+" currency) and ("[^"]+" channel)$/
235
     */
236
    public function iSetItsPriceToForCurrencyAndChannel($price, CurrencyInterface $currency, ChannelInterface $channel)
237
    {
238
        $this->createSimpleProductPage->specifyPriceForChannelAndCurrency($price, $channel, $currency);
239
    }
240
241
    /**
242
     * @When I set its slug to :slug
243
     * @When I remove its slug
244
     */
245
    public function iSetItsSlugTo($slug = null)
246
    {
247
        $this->createSimpleProductPage->specifySlug($slug);
248
    }
249
250
    /**
251
     * @When I enable slug modification
252
     */
253
    public function iEnableSlugModification()
254
    {
255
        $this->createSimpleProductPage->enableSlugModification();
256
    }
257
258
    /**
259
     * @Then the product :productName should appear in the store
260
     * @Then the product :productName should be in the shop
261
     * @Then this product should still be named :productName
262
     */
263
    public function theProductShouldAppearInTheShop($productName)
264
    {
265
        $this->iWantToBrowseProducts();
266
267
        Assert::true(
268
            $this->indexPage->isSingleResourceOnPage(['name' => $productName]),
269
            sprintf('The product with name %s has not been found.', $productName)
270
        );
271
    }
272
273
    /**
274
     * @Given I am browsing products
275
     * @When I want to browse products
276
     */
277
    public function iWantToBrowseProducts()
278
    {
279
        $this->indexPage->open();
280
    }
281
282
    /**
283
     * @When /^I am browsing products from ("([^"]+)" taxon)$/
284
     */
285
    public function iAmBrowsingProductsFromTaxon(TaxonInterface $taxon)
286
    {
287
        $this->indexPerTaxonPage->open(['taxonId' => $taxon->getId()]);
288
    }
289
290
    /**
291
     * @When I filter them by :taxonName taxon
292
     */
293
    public function iFilterThemByTaxon($taxonName)
294
    {
295
        $this->indexPage->filterByTaxon($taxonName);
296
    }
297
298
    /**
299
     * @Then I should( still) see a product with :field :value
300
     */
301
    public function iShouldSeeProductWith($field, $value)
302
    {
303
        Assert::true(
304
            $this->indexPage->isSingleResourceOnPage([$field => $value]),
305
            sprintf('The product with %s "%s" has not been found.', $field, $value)
306
        );
307
    }
308
309
    /**
310
     * @Then I should not see any product with :field :value
311
     */
312
    public function iShouldNotSeeAnyProductWith($field, $value)
313
    {
314
        Assert::false(
315
            $this->indexPage->isSingleResourceOnPage([$field => $value]),
316
            sprintf('The product with %s "%s" has been found.', $field, $value)
317
        );
318
    }
319
320
    /**
321
     * @Then the first product on the list should have :field :value
322
     */
323
    public function theFirstProductOnTheListShouldHave($field, $value)
324
    {
325
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
326
            $this->indexPage,
327
            $this->indexPerTaxonPage,
328
        ]);
329
330
        $actualValue = $currentPage->getColumnFields($field)[0];
331
332
        Assert::same(
333
            $actualValue,
334
            $value,
335
            sprintf('Expected first product\'s %s to be "%s", but it is "%s".', $field, $value, $actualValue)
336
        );
337
    }
338
339
    /**
340
     * @Then the last product on the list should have :field :value
341
     */
342
    public function theLastProductOnTheListShouldHave($field, $value)
343
    {
344
        $columnFields = $this->indexPerTaxonPage->getColumnFields($field);
345
        $actualValue = end($columnFields);
346
347
        Assert::same(
348
            $actualValue,
349
            $value,
350
            sprintf('Expected last product\'s %s to be "%s", but it is "%s".', $field, $value, $actualValue)
351
        );
352
    }
353
354
    /**
355
     * @When I switch the way products are sorted by :field
356
     * @When I start sorting products by :field
357
     * @Given the products are already sorted by :field
358
     */
359
    public function iSortProductsBy($field)
360
    {
361
        $this->indexPage->sortBy($field);
362
    }
363
364
    /**
365
     * @Then I should see :numberOfProducts products in the list
366
     */
367
    public function iShouldSeeProductsInTheList($numberOfProducts)
368
    {
369
        $foundRows = $this->indexPage->countItems();
370
371
        Assert::same(
372
            (int) $numberOfProducts,
373
            $foundRows,
374
            '%s rows with products should appear on page, %s rows has been found'
375
        );
376
    }
377
378
    /**
379
     * @When I delete the :product product
380
     * @When I try to delete the :product product
381
     */
382
    public function iDeleteProduct(ProductInterface $product)
383
    {
384
        $this->sharedStorage->set('product', $product);
385
386
        $this->iWantToBrowseProducts();
387
        $this->indexPage->deleteResourceOnPage(['name' => $product->getName()]);
388
    }
389
390
    /**
391
     * @Then /^(this product) should not exist in the product catalog$/
392
     */
393
    public function productShouldNotExist(ProductInterface $product)
394
    {
395
        $this->iWantToBrowseProducts();
396
397
        Assert::false(
398
            $this->indexPage->isSingleResourceOnPage(['code' => $product->getCode()]),
399
            sprintf('Product with code %s exists but should not.', $product->getCode())
400
        );
401
    }
402
403
    /**
404
     * @Then I should be notified that this product is in use and cannot be deleted
405
     */
406
    public function iShouldBeNotifiedOfFailure()
407
    {
408
        $this->notificationChecker->checkNotification(
409
            "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...
410
            NotificationType::failure()
411
        );
412
    }
413
414
    /**
415
     * @Then /^(this product) should still exist in the product catalog$/
416
     */
417
    public function productShouldExistInTheProductCatalog(ProductInterface $product)
418
    {
419
        $this->theProductShouldAppearInTheShop($product->getName());
420
    }
421
422
    /**
423
     * @When I want to modify the :product product
424
     * @When /^I want to modify (this product)$/
425
     */
426
    public function iWantToModifyAProduct(ProductInterface $product)
427
    {
428
        $this->sharedStorage->set('product', $product);
429
430
        if ($product->isSimple()) {
431
            $this->updateSimpleProductPage->open(['id' => $product->getId()]);
432
            return;
433
        }
434
435
        $this->updateConfigurableProductPage->open(['id' => $product->getId()]);
436
    }
437
438
    /**
439
     * @Then the code field should be disabled
440
     */
441
    public function theCodeFieldShouldBeDisabled()
442
    {
443
        /** @var UpdatePageInterface $currentPage */
444
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
445
            $this->updateSimpleProductPage,
446
            $this->updateConfigurableProductPage,
447
        ], $this->sharedStorage->get('product'));
448
449
        Assert::true(
450
            $currentPage->isCodeDisabled(),
451
            'Code should be immutable, but it does not.'
452
        );
453
    }
454
455
    /**
456
     * @Then the slug field should not be editable
457
     */
458
    public function theSlugFieldShouldNotBeEditable()
459
    {
460
        Assert::true(
461
            $this->updateSimpleProductPage->isSlugReadOnly(),
462
            'Slug should be immutable, but it does not.'
463
        );
464
    }
465
466
    /**
467
     * @Then /^this product price should be "(?:€|£|\$)([^"]+)"$/
468
     */
469
    public function thisProductPriceShouldBeEqualTo($price)
470
    {
471
        $this->assertElementValue('price', $price);
472
    }
473
474
    /**
475
     * @Then this product name should be :name
476
     */
477
    public function thisProductElementShouldBe($name)
478
    {
479
        $this->assertElementValue('name', $name);
480
    }
481
482
    /**
483
     * @Then /^I should be notified that (code|name|slug) is required$/
484
     */
485
    public function iShouldBeNotifiedThatIsRequired($element)
486
    {
487
        $this->assertValidationMessage($element, sprintf('Please enter product %s.', $element));
488
    }
489
490
    /**
491
     * @Then I should be notified that price is required
492
     */
493
    public function iShouldBeNotifiedThatPriceIsRequired()
494
    {
495
        $this->assertValidationMessage('price', 'Please enter the price.');
496
    }
497
498
    /**
499
     * @When I save my changes
500
     * @When I try to save my changes
501
     */
502
    public function iSaveMyChanges()
503
    {
504
        /** @var UpdatePageInterface $currentPage */
505
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
506
            $this->updateSimpleProductPage,
507
            $this->updateConfigurableProductPage,
508
        ], $this->sharedStorage->get('product'));
509
510
        Assert::isInstanceOf($currentPage, UpdatePageInterface::class);
511
512
        $currentPage->saveChanges();
513
    }
514
515
    /**
516
     * @When /^I change its price to "(?:€|£|\$)([^"]+)"$/
517
     */
518
    public function iChangeItsPriceTo($price)
519
    {
520
        $this->updateSimpleProductPage->specifyPrice($price);
521
    }
522
523
    /**
524
     * @Given I add the :optionName option to it
525
     */
526
    public function iAddTheOptionToIt($optionName)
527
    {
528
        $this->createConfigurableProductPage->selectOption($optionName);
529
    }
530
531
    /**
532
     * @When I set its :attribute attribute to :value
533
     */
534
    public function iSetItsAttributeTo($attribute, $value)
535
    {
536
        $this->createSimpleProductPage->addAttribute($attribute, $value);
537
    }
538
539
    /**
540
     * @When I remove its :attribute attribute
541
     */
542
    public function iRemoveItsAttribute($attribute)
543
    {
544
        $this->createSimpleProductPage->removeAttribute($attribute);
545
    }
546
547
    /**
548
     * @Then /^attribute "([^"]+)" of (product "[^"]+") should be "([^"]+)"$/
549
     */
550
    public function itsAttributeShouldBe($attribute, ProductInterface $product, $value)
551
    {
552
        $this->updateSimpleProductPage->open(['id' => $product->getId()]);
553
554
        Assert::same(
555
            $value,
556
            $this->updateSimpleProductPage->getAttributeValue($attribute),
557
            sprintf('ProductAttribute "%s" should have value "%s" but it does not.', $attribute, $value)
558
        );
559
    }
560
561
    /**
562
     * @Then /^(product "[^"]+") should not have a "([^"]+)" attribute$/
563
     */
564
    public function productShouldNotHaveAttribute(ProductInterface $product, $attribute)
565
    {
566
        $this->updateSimpleProductPage->open(['id' => $product->getId()]);
567
568
        Assert::false(
569
            $this->updateSimpleProductPage->hasAttribute($attribute),
570
            sprintf('Product "%s" should not have attribute "%s" but it does.', $product->getName(), $attribute)
571
        );
572
    }
573
574
    /**
575
     * @Given product with :element :value should not be added
576
     */
577
    public function productWithNameShouldNotBeAdded($element, $value)
578
    {
579
        $this->iWantToBrowseProducts();
580
581
        Assert::false(
582
            $this->indexPage->isSingleResourceOnPage([$element => $value]),
583
            sprintf('Product with %s %s was created, but it should not.', $element, $value)
584
        );
585
    }
586
587
    /**
588
     * @When I remove its name from :language translation
589
     */
590
    public function iRemoveItsNameFromTranslation($language)
591
    {
592
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
593
            $this->updateSimpleProductPage,
594
            $this->updateConfigurableProductPage,
595
        ], $this->sharedStorage->get('product'));
596
597
        $currentPage->nameItIn('', $language);
598
    }
599
600
    /**
601
     * @Then /^this product should have (?:a|an) "([^"]+)" option$/
602
     */
603
    public function thisProductShouldHaveOption($productOption)
604
    {
605
        $this->updateConfigurableProductPage->isProductOptionChosen($productOption);
606
    }
607
608
    /**
609
     * @Then the option field should be disabled
610
     */
611
    public function theOptionFieldShouldBeDisabled()
612
    {
613
        Assert::true(
614
            $this->updateConfigurableProductPage->isProductOptionsDisabled(),
615
            'Options field should be immutable, but it does not.'
616
        );
617
    }
618
619
    /**
620
     * @When /^I choose main (taxon "([^"]+)")$/
621
     */
622
    public function iChooseMainTaxon(TaxonInterface $taxon)
623
    {
624
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
625
            $this->updateSimpleProductPage,
626
            $this->updateConfigurableProductPage,
627
        ], $this->sharedStorage->get('product'));
628
629
        $currentPage->selectMainTaxon($taxon);
630
    }
631
632
    /**
633
     * @Then /^the slug of the ("[^"]+" product) should(?:| still) be "([^"]+)"$/
634
     */
635
    public function productSlugShouldBe(ProductInterface $product, $slug)
636
    {
637
        $this->updateSimpleProductPage->open(['id' => $product->getId()]);
638
639
        Assert::true(
640
            $this->updateSimpleProductPage->hasResourceValues(['slug' => $slug]),
641
            sprintf('Product\'s slug should be %s.', $slug)
642
        );
643
    }
644
645
    /**
646
     * @Then /^(this product) main taxon should be "([^"]+)"$/
647
     */
648
    public function thisProductMainTaxonShouldBe(ProductInterface $product, $taxonName)
649
    {
650
        /** @var UpdatePageInterface $currentPage */
651
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
652
            $this->updateSimpleProductPage,
653
            $this->updateConfigurableProductPage,
654
        ], $this->sharedStorage->get('product'));
655
656
        $currentPage->open(['id' => $product->getId()]);
657
658
        Assert::true(
659
            $this->updateConfigurableProductPage->isMainTaxonChosen($taxonName),
660
            sprintf('The main taxon %s should be chosen, but it does not.', $taxonName)
661
        );
662
    }
663
664
    /**
665
     * @Then /^inventory of (this product) should not be tracked$/
666
     */
667
    public function thisProductShouldNotBeTracked(ProductInterface $product)
668
    {
669
        $this->iWantToModifyAProduct($product);
670
671
        Assert::false(
672
            $this->updateSimpleProductPage->isTracked(),
673
            '"%s" should not be tracked, but it is.'
674
        );
675
    }
676
677
    /**
678
     * @Then /^inventory of (this product) should be tracked$/
679
     */
680
    public function thisProductShouldBeTracked(ProductInterface $product)
681
    {
682
        $this->iWantToModifyAProduct($product);
683
684
        Assert::true(
685
            $this->updateSimpleProductPage->isTracked(),
686
            '"%s" should be tracked, but it is not.'
687
        );
688
    }
689
690
    /**
691
     * @When I attach the :path image with a code :code
692
     */
693
    public function iAttachImageWithACode($path, $code)
694
    {
695
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
696
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
697
            $this->createSimpleProductPage,
698
            $this->createConfigurableProductPage,
699
            $this->updateSimpleProductPage,
700
            $this->updateConfigurableProductPage,
701
        ], $this->sharedStorage->has('product') ? $this->sharedStorage->get('product') : null);
702
703
        $currentPage->attachImage($path, $code);
704
    }
705
706
    /**
707
     * @When I attach the :path image without a code
708
     */
709
    public function iAttachImageWithoutACode($path)
710
    {
711
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
712
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
713
            $this->updateSimpleProductPage,
714
            $this->updateConfigurableProductPage,
715
        ], $this->sharedStorage->get('product'));
716
717
        $currentPage->attachImage($path);
718
    }
719
720
    /**
721
     * @When I associate as :productAssociationType the :productName product
722
     * @When I associate as :productAssociationType the :firstProductName and :secondProductName products
723
     */
724
    public function iAssociateProductsAsProductAssociation(
725
        ProductAssociationTypeInterface $productAssociationType,
726
        ...$productsNames
727
    ) {
728
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
729
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
730
            $this->createSimpleProductPage,
731
            $this->updateSimpleProductPage,
732
        ], $this->sharedStorage->get('product'));
733
734
        $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...
735
    }
736
737
    /**
738
     * @When I remove an associated product :productName from :productAssociationType
739
     */
740
    public function iRemoveAnAssociatedProductFromProductAssociation(
741
        $productName,
742
        ProductAssociationTypeInterface $productAssociationType
743
    ) {
744
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
745
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
746
            $this->createSimpleProductPage,
747
            $this->updateSimpleProductPage,
748
        ], $this->sharedStorage->get('product'));
749
750
        $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...
751
    }
752
753
    /**
754
     * @Then /^(this product) should have(?:| also) an image with a code "([^"]*)"$/
755
     * @Then /^the (product "[^"]+") should have(?:| also) an image with a code "([^"]*)"$/
756
     */
757
    public function thisProductShouldHaveAnImageWithCode(ProductInterface $product, $code)
758
    {
759
        $this->sharedStorage->set('product', $product);
760
761
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
762
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
763
            $this->updateSimpleProductPage,
764
            $this->updateConfigurableProductPage,
765
        ], $product);
766
767
        Assert::true(
768
            $currentPage->isImageWithCodeDisplayed($code),
769
            sprintf('Image with a code %s should have been displayed.', $code)
770
        );
771
    }
772
773
    /**
774
     * @Then /^(this product) should not have(?:| also) an image with a code "([^"]*)"$/
775
     */
776
    public function thisProductShouldNotHaveAnImageWithCode(ProductInterface $product, $code)
777
    {
778
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
779
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
780
            $this->updateSimpleProductPage,
781
            $this->updateConfigurableProductPage,
782
        ], $product);
783
784
        Assert::false(
785
            $currentPage->isImageWithCodeDisplayed($code),
786
            sprintf('Image with a code %s should not have been displayed.', $code)
787
        );
788
    }
789
790
    /**
791
     * @When I change the image with the :code code to :path
792
     */
793
    public function iChangeItsImageToPathForTheCode($path, $code)
794
    {
795
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
796
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
797
            $this->updateSimpleProductPage,
798
            $this->updateConfigurableProductPage,
799
        ], $this->sharedStorage->get('product'));
800
801
        $currentPage->changeImageWithCode($code, $path);
802
    }
803
804
    /**
805
     * @When /^I remove(?:| also) an image with a code "([^"]*)"$/
806
     */
807
    public function iRemoveAnImageWithACode($code)
808
    {
809
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
810
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
811
            $this->updateSimpleProductPage,
812
            $this->updateConfigurableProductPage,
813
        ], $this->sharedStorage->get('product'));
814
815
        $currentPage->removeImageWithCode($code);
816
    }
817
818
    /**
819
     * @When I remove the first image
820
     */
821
    public function iRemoveTheFirstImage()
822
    {
823
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
824
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
825
            $this->updateSimpleProductPage,
826
            $this->updateConfigurableProductPage,
827
        ], $this->sharedStorage->get('product'));
828
829
        $currentPage->removeFirstImage();
830
    }
831
832
    /**
833
     * @Then /^(this product) should not have any images$/
834
     */
835
    public function thisProductShouldNotHaveImages(ProductInterface $product)
836
    {
837
        $this->iWantToModifyAProduct($product);
838
839
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
840
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
841
            $this->updateSimpleProductPage,
842
            $this->updateConfigurableProductPage,
843
        ], $this->sharedStorage->get('product'));
844
845
        Assert::same(
846
            0,
847
            $currentPage->countImages(),
848
            'This product has %2$s, but it should not have.'
849
        );
850
    }
851
852
    /**
853
     * @Then the image code field should be disabled
854
     */
855
    public function theImageCodeFieldShouldBeDisabled()
856
    {
857
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
858
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
859
            $this->updateSimpleProductPage,
860
            $this->updateConfigurableProductPage,
861
        ], $this->sharedStorage->get('product'));
862
863
        Assert::true(
864
            $currentPage->isImageCodeDisabled(),
865
            'Image code field should be disabled but it is not.'
866
        );
867
    }
868
869
    /**
870
     * @Then I should be notified that the image with this code already exists
871
     */
872
    public function iShouldBeNotifiedThatTheImageWithThisCodeAlreadyExists()
873
    {
874
        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...
875
    }
876
877
    /**
878
     * @Then I should be notified that an image code is required
879
     */
880
    public function iShouldBeNotifiedThatAnImageCodeIsRequired()
881
    {
882
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
883
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
884
            $this->updateSimpleProductPage,
885
            $this->updateConfigurableProductPage,
886
        ], $this->sharedStorage->get('product'));
887
888
        Assert::same(
889
            $currentPage->getValidationMessageForImage(),
890
            'Please enter an image code.'
891
        );
892
    }
893
894
    /**
895
     * @Then there should still be only one image in the :product product
896
     */
897
    public function thereShouldStillBeOnlyOneImageInThisTaxon(ProductInterface $product)
898
    {
899
        $this->iWantToModifyAProduct($product);
900
901
        /** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */
902
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
903
            $this->updateSimpleProductPage,
904
            $this->updateConfigurableProductPage,
905
        ], $product);
906
907
        Assert::same(
908
            1,
909
            $currentPage->countImages(),
910
            'This product has %2$s images, but it should have only one.'
911
        );
912
    }
913
914
    /**
915
     * @Then /^there should be no reviews of (this product)$/
916
     */
917
    public function thereAreNoProductReviews(ProductInterface $product)
918
    {
919
        $this->productReviewIndexPage->open();
920
921
        Assert::false(
922
            $this->productReviewIndexPage->isSingleResourceOnPage(['reviewSubject' => $product->getName()]),
923
            sprintf('There should be no reviews of %s.', $product->getName())
924
        );
925
    }
926
927
    /**
928
     * @Then /^the product for ("[^"]+" currency) and ("[^"]+" channel) should be priced at "(?:€|£|\$)([^"]+)"$/
929
     */
930
    public function theProductForCurrencyAndChannelShouldBePricedAt(CurrencyInterface $currency, ChannelInterface $channel, $price)
931
    {
932
        Assert::same(
933
            $this->updateSimpleProductPage->getPricingConfigurationForChannelAndCurrencyCalculator($channel, $currency),
934
            $price
935
        );
936
    }
937
938
    /**
939
     * @Then this product should( also) have an association :productAssociationType with product :productName
940
     * @Then this product should( also) have an association :productAssociationType with products :firstProductName and :secondProductName
941
     */
942
    public function theProductShouldHaveAnAssociationWithProducts(
943
        ProductAssociationTypeInterface $productAssociationType,
944
        ...$productsNames
945
    ) {
946
        foreach ($productsNames as $productName) {
947
            Assert::true(
948
                $this->updateSimpleProductPage->hasAssociatedProduct($productName, $productAssociationType),
949
                sprintf(
950
                    'This product should have an association %s with product %s, but it does not.',
951
                    $productAssociationType->getName(),
952
                    $productName
953
                )
954
            );
955
        }
956
    }
957
958
    /**
959
     * @Then this product should not have an association :productAssociationType with product :productName
960
     */
961
    public function theProductShouldNotHaveAnAssociationWithProducts(
962
        ProductAssociationTypeInterface $productAssociationType,
963
        $productName
964
    ) {
965
        Assert::false(
966
            $this->updateSimpleProductPage->hasAssociatedProduct($productName, $productAssociationType),
967
            sprintf(
968
                'This product should not have an association %s with product %s, but it does.',
969
                $productAssociationType->getName(),
970
                $productName
971
            )
972
        );
973
    }
974
975
    /**
976
     * @Then I should be notified that simple product code has to be unique
977
     */
978
    public function iShouldBeNotifiedThatSimpleProductCodeHasToBeUnique()
979
    {
980
        $this->assertValidationMessage('code', 'Simple product code must be unique among all products and product variants.');
981
    }
982
983
    /**
984
     * @Then I should be notified that code has to be unique
985
     */
986
    public function iShouldBeNotifiedThatCodeHasToBeUnique()
987
    {
988
        $this->assertValidationMessage('code', 'Product code must be unique.');
989
    }
990
991
    /**
992
     * @Then they should have order like :firstProductName, :secondProductName and :thirdProductName
993
     */
994
    public function theyShouldHaveOrderLikeAnd(...$productNames)
995
    {
996
        Assert::true(
997
            $this->indexPerTaxonPage->hasProductsInOrder($productNames),
998
            'The products have wrong order.'
999
        );
1000
    }
1001
1002
    /**
1003
     * @When I set the position of :productName to :position
1004
     */
1005
    public function iSetThePositionOfTo($productName, $position)
1006
    {
1007
        $this->indexPerTaxonPage->setPositionOfProduct($productName, (int) $position);
1008
    }
1009
1010
    /**
1011
     * @param string $element
1012
     * @param string $value
1013
     */
1014
    private function assertElementValue($element, $value)
1015
    {
1016
        /** @var UpdatePageInterface $currentPage */
1017
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
1018
            $this->updateSimpleProductPage,
1019
            $this->updateConfigurableProductPage,
1020
        ], $this->sharedStorage->get('product'));
1021
1022
        Assert::isInstanceOf($currentPage, UpdatePageInterface::class);
1023
1024
        Assert::true(
1025
            $currentPage->hasResourceValues(
1026
                [$element => $value]
1027
            ),
1028
            sprintf('Product should have %s with %s value.', $element, $value)
1029
        );
1030
    }
1031
1032
    /**
1033
     * @param string $element
1034
     * @param string $message
1035
     */
1036
    private function assertValidationMessage($element, $message)
1037
    {
1038
        $product = $this->sharedStorage->has('product') ? $this->sharedStorage->get('product') : null;
1039
1040
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
1041
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([
1042
            $this->createSimpleProductPage,
1043
            $this->createConfigurableProductPage,
1044
            $this->updateSimpleProductPage,
1045
            $this->updateConfigurableProductPage,
1046
        ], $product);
1047
1048
        Assert::same($currentPage->getValidationMessage($element), $message);
1049
    }
1050
1051
    /**
1052
     * @When I save my new configuration
1053
     */
1054
    public function iSaveMyNewConfiguration()
1055
    {
1056
        $this->indexPerTaxonPage->savePositions();
1057
    }
1058
}
1059