Completed
Push — 1.7 ( 0983e5...497e44 )
by Kamil
05:26
created

iShouldBeNotifiedThatThisVariantAlreadyExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
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
declare(strict_types=1);
13
14
namespace Sylius\Behat\Context\Ui\Admin;
15
16
use Behat\Behat\Context\Context;
17
use Sylius\Behat\NotificationType;
18
use Sylius\Behat\Page\Admin\ProductVariant\CreatePageInterface;
19
use Sylius\Behat\Page\Admin\ProductVariant\GeneratePageInterface;
20
use Sylius\Behat\Page\Admin\ProductVariant\IndexPageInterface;
21
use Sylius\Behat\Page\Admin\ProductVariant\UpdatePageInterface;
22
use Sylius\Behat\Service\NotificationCheckerInterface;
23
use Sylius\Behat\Service\Resolver\CurrentPageResolverInterface;
24
use Sylius\Behat\Service\SharedStorageInterface;
25
use Sylius\Component\Core\Model\ProductInterface;
26
use Sylius\Component\Core\Model\ProductVariantInterface;
27
use Webmozart\Assert\Assert;
28
29
final class ManagingProductVariantsContext implements Context
30
{
31
    /** @var SharedStorageInterface */
32
    private $sharedStorage;
33
34
    /** @var CreatePageInterface */
35
    private $createPage;
36
37
    /** @var IndexPageInterface */
38
    private $indexPage;
39
40
    /** @var UpdatePageInterface */
41
    private $updatePage;
42
43
    /** @var GeneratePageInterface */
44
    private $generatePage;
45
46
    /** @var CurrentPageResolverInterface */
47
    private $currentPageResolver;
48
49
    /** @var NotificationCheckerInterface */
50
    private $notificationChecker;
51
52
    public function __construct(
53
        SharedStorageInterface $sharedStorage,
54
        CreatePageInterface $createPage,
55
        IndexPageInterface $indexPage,
56
        UpdatePageInterface $updatePage,
57
        GeneratePageInterface $generatePage,
58
        CurrentPageResolverInterface $currentPageResolver,
59
        NotificationCheckerInterface $notificationChecker
60
    ) {
61
        $this->sharedStorage = $sharedStorage;
62
        $this->createPage = $createPage;
63
        $this->indexPage = $indexPage;
64
        $this->updatePage = $updatePage;
65
        $this->generatePage = $generatePage;
66
        $this->currentPageResolver = $currentPageResolver;
67
        $this->notificationChecker = $notificationChecker;
68
    }
69
70
    /**
71
     * @Given /^I want to create a new variant of (this product)$/
72
     */
73
    public function iWantToCreateANewProduct(ProductInterface $product)
74
    {
75
        $this->createPage->open(['productId' => $product->getId()]);
76
    }
77
78
    /**
79
     * @When I specify its code as :code
80
     * @When I do not specify its code
81
     */
82
    public function iSpecifyItsCodeAs($code = null)
83
    {
84
        $this->createPage->specifyCode($code ?? '');
85
    }
86
87
    /**
88
     * @When I name it :name in :language
89
     */
90
    public function iNameItIn($name, $language)
91
    {
92
        $this->createPage->nameItIn($name, $language);
93
    }
94
95
    /**
96
     * @When I add it
97
     * @When I try to add it
98
     */
99
    public function iAddIt()
100
    {
101
        $this->createPage->create();
102
    }
103
104
    /**
105
     * @When I change its :optionName option to :optionValue
106
     */
107
    public function iChangeItsOptionTo(string $optionName, string $optionValue): void
108
    {
109
        $this->updatePage->selectOption(strtoupper($optionName), $optionValue);
110
    }
111
112
    /**
113
     * @When I disable its inventory tracking
114
     */
115
    public function iDisableItsTracking()
116
    {
117
        $this->updatePage->disableTracking();
118
    }
119
120
    /**
121
     * @When I enable its inventory tracking
122
     */
123
    public function iEnableItsTracking()
124
    {
125
        $this->updatePage->enableTracking();
126
    }
127
128
    /**
129
     * @When /^I set its(?:| default) price to "(?:€|£|\$)([^"]+)" for "([^"]+)" channel$/
130
     * @When I do not set its price
131
     */
132
    public function iSetItsPriceTo(?string $price = null, $channelName = null)
133
    {
134
        $this->createPage->specifyPrice($price ?? '', $channelName ?? (string) $this->sharedStorage->get('channel'));
135
    }
136
137
    /**
138
     * @When I remove its price for :channel channel
139
     */
140
    public function iRemoveItsPriceForChannel(string $channelName): void
141
    {
142
        $this->iSetItsPriceTo('',  $channelName);
143
    }
144
145
    /**
146
     * @When /^I set its original price to "(?:€|£|\$)([^"]+)" for "([^"]+)" channel$/
147
     */
148
    public function iSetItsOriginalPriceTo(string $originalPrice, string $channelName): void
149
    {
150
        $this->createPage->specifyOriginalPrice($originalPrice, $channelName);
151
    }
152
153
    /**
154
     * @When I set its height, width, depth and weight to :number
155
     */
156
    public function iSetItsDimensionsTo($value)
157
    {
158
        $this->createPage->specifyHeightWidthDepthAndWeight($value, $value, $value, $value);
159
    }
160
161
    /**
162
     * @When I do not specify its current stock
163
     */
164
    public function iDoNetSetItsCurrentStockTo()
165
    {
166
        $this->createPage->specifyCurrentStock('');
167
    }
168
169
    /**
170
     * @When I choose :calculatorName calculator
171
     */
172
    public function iChooseCalculator($calculatorName)
173
    {
174
        $this->createPage->choosePricingCalculator($calculatorName);
175
    }
176
177
    /**
178
     * @When I set its :optionName option to :optionValue
179
     */
180
    public function iSetItsOptionAs($optionName, $optionValue)
181
    {
182
        $this->createPage->selectOption($optionName, $optionValue);
183
    }
184
185
    /**
186
     * @When I set the position of :name to :position
187
     */
188
    public function iSetThePositionOfTo($name, int $position)
189
    {
190
        $this->indexPage->setPosition($name, $position);
191
    }
192
193
    /**
194
     * @When I save my new configuration
195
     */
196
    public function iSaveMyNewConfiguration()
197
    {
198
        $this->indexPage->savePositions();
199
    }
200
201
    /**
202
     * @When I do not want to have shipping required for this product
203
     */
204
    public function iDoNotWantToHaveShippingRequiredForThisProduct()
205
    {
206
        $this->createPage->setShippingRequired(false);
207
    }
208
209
    /**
210
     * @When I check (also) the :productVariantName product variant
211
     */
212
    public function iCheckTheProductVariantName(string $productVariantName): void
213
    {
214
        $this->indexPage->checkResourceOnPage(['name' => $productVariantName]);
215
    }
216
217
    /**
218
     * @When I delete them
219
     */
220
    public function iDeleteThem(): void
221
    {
222
        $this->indexPage->bulkDelete();
223
    }
224
225
    /**
226
     * @Then /^the (variant with code "[^"]+") should be priced at (?:€|£|\$)([^"]+) for channel "([^"]+)"$/
227
     */
228
    public function theVariantWithCodeShouldBePricedAtForChannel(ProductVariantInterface $productVariant, string $price, $channelName)
229
    {
230
        $this->updatePage->open(['id' => $productVariant->getId(), 'productId' => $productVariant->getProduct()->getId()]);
231
232
        Assert::same($this->updatePage->getPriceForChannel($channelName), $price);
233
    }
234
235
    /**
236
     * @Then /^the (variant with code "[^"]+") should be named "([^"]+)" in ("([^"]+)" locale)$/
237
     */
238
    public function theVariantWithCodeShouldBeNamedIn(ProductVariantInterface $productVariant, $name, $language)
239
    {
240
        $this->updatePage->open(['id' => $productVariant->getId(), 'productId' => $productVariant->getProduct()->getId()]);
241
242
        Assert::same($this->updatePage->getNameInLanguage($language), $name);
243
    }
244
245
    /**
246
     * @Then /^the (variant with code "[^"]+") should have an original price of (?:€|£|\$)([^"]+) for channel "([^"]+)"$/
247
     */
248
    public function theVariantWithCodeShouldHaveAnOriginalPriceOfForChannel(ProductVariantInterface $productVariant, $originalPrice, $channelName)
249
    {
250
        $this->updatePage->open(['id' => $productVariant->getId(), 'productId' => $productVariant->getProduct()->getId()]);
251
252
        Assert::same(
253
            $this->updatePage->getOriginalPriceForChannel($channelName),
254
            $originalPrice
255
        );
256
    }
257
258
    /**
259
     * @When /^I delete the ("[^"]+" variant of product "[^"]+")$/
260
     * @When /^I try to delete the ("[^"]+" variant of product "[^"]+")$/
261
     */
262
    public function iDeleteTheVariantOfProduct(ProductVariantInterface $productVariant)
263
    {
264
        $this->indexPage->open(['productId' => $productVariant->getProduct()->getId()]);
265
266
        $this->indexPage->deleteResourceOnPage(['code' => $productVariant->getCode()]);
267
    }
268
269
    /**
270
     * @Then I should be notified that this variant is in use and cannot be deleted
271
     */
272
    public function iShouldBeNotifiedOfFailure()
273
    {
274
        $this->notificationChecker->checkNotification(
275
            'Cannot delete, the product variant is in use.',
276
            NotificationType::failure()
277
        );
278
    }
279
280
    /**
281
     * @When /^I want to modify the ("[^"]+" product variant)$/
282
     */
283
    public function iWantToModifyAProduct(ProductVariantInterface $productVariant)
284
    {
285
        $this->updatePage->open(['id' => $productVariant->getId(), 'productId' => $productVariant->getProduct()->getId()]);
286
    }
287
288
    /**
289
     * @Then the code field should be disabled
290
     */
291
    public function theCodeFieldShouldBeDisabled()
292
    {
293
        Assert::true($this->updatePage->isCodeDisabled());
294
    }
295
296
    /**
297
     * @Then I should be notified that :element is required
298
     */
299
    public function iShouldBeNotifiedThatIsRequired($element)
300
    {
301
        $this->assertValidationMessage($element, sprintf('Please enter the %s.', $element));
302
    }
303
304
    /**
305
     * @Then I should be notified that code has to be unique
306
     */
307
    public function iShouldBeNotifiedThatCodeHasToBeUnique()
308
    {
309
        $this->assertValidationMessage('code', 'Product variant code must be unique.');
310
    }
311
312
    /**
313
     * @Then I should be notified that current stock is required
314
     */
315
    public function iShouldBeNotifiedThatOnHandIsRequired()
316
    {
317
        $this->assertValidationMessage('on_hand', 'Please enter on hand.');
318
    }
319
320
    /**
321
     * @Then I should be notified that height, width, depth and weight cannot be lower than 0
322
     */
323
    public function iShouldBeNotifiedThatIsHeightWidthDepthWeightCannotBeLowerThan()
324
    {
325
        $this->assertValidationMessage('height', 'Height cannot be negative.');
326
        $this->assertValidationMessage('width', 'Width cannot be negative.');
327
        $this->assertValidationMessage('depth', 'Depth cannot be negative.');
328
        $this->assertValidationMessage('weight', 'Weight cannot be negative.');
329
    }
330
331
    /**
332
     * @Then I should be notified that price cannot be lower than 0
333
     */
334
    public function iShouldBeNotifiedThatPriceCannotBeLowerThen(): void
335
    {
336
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
337
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
338
339
        Assert::contains($currentPage->getPricesValidationMessage(), 'Price cannot be lower than 0.');
0 ignored issues
show
Bug introduced by
The method getPricesValidationMessage does only exist in Sylius\Behat\Page\Admin\...ant\CreatePageInterface, but not in Sylius\Behat\Page\Admin\...ant\UpdatePageInterface.

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...
340
    }
341
342
    /**
343
     * @Then I should be notified that this variant already exists
344
     */
345
    public function iShouldBeNotifiedThatThisVariantAlreadyExists()
346
    {
347
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
348
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
349
350
        Assert::same($currentPage->getValidationMessageForForm(), 'Variant with this option set already exists.');
0 ignored issues
show
Bug introduced by
The method getValidationMessageForForm does only exist in Sylius\Behat\Page\Admin\...ant\CreatePageInterface, but not in Sylius\Behat\Page\Admin\...ant\UpdatePageInterface.

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...
351
    }
352
353
    /**
354
     * @Then /^I should be notified that code is required for the (\d)(?:st|nd|rd|th) variant$/
355
     */
356
    public function iShouldBeNotifiedThatCodeIsRequiredForVariant($position)
357
    {
358
        Assert::same(
359
            $this->generatePage->getValidationMessage('code', $position - 1),
360
            'Please enter the code.'
361
        );
362
    }
363
364
    /**
365
     * @Then /^I should be notified that prices in all channels must be defined for the (\d)(?:st|nd|rd|th) variant$/
366
     */
367
    public function iShouldBeNotifiedThatPricesInAllChannelsMustBeDefinedForTheVariant($position)
368
    {
369
        Assert::same(
370
            $this->generatePage->getPricesValidationMessage($position - 1),
371
            'You must define price for every channel.'
372
        );
373
    }
374
375
    /**
376
     * @Then /^I should be notified that variant code must be unique within this product for the (\d)(?:st|nd|rd|th) variant$/
377
     */
378
    public function iShouldBeNotifiedThatVariantCodeMustBeUniqueWithinThisProductForYheVariant($position)
379
    {
380
        Assert::same(
381
            $this->generatePage->getValidationMessage('code', $position - 1),
382
            'This code must be unique within this product.'
383
        );
384
    }
385
386
    /**
387
     * @Then I should be notified that prices in all channels must be defined
388
     */
389
    public function iShouldBeNotifiedThatPricesInAllChannelsMustBeDefined()
390
    {
391
        Assert::contains(
392
            $this->createPage->getPricesValidationMessage(),
393
            'You must define price for every channel.'
394
        );
395
    }
396
397
    /**
398
     * @When I choose to show this product in the :channel channel
399
     */
400
    public function iChooseToShowThisProductInTheChannel(string $channel): void
401
    {
402
        $this->updatePage->showProductInChannel($channel);
403
    }
404
405
    /**
406
     * @When I choose to show this product in this channel
407
     */
408
    public function iChooseToShowThisProductInThisChannel(): void
409
    {
410
        $this->updatePage->showProductInSingleChannel();
411
    }
412
413
    /**
414
     * @When I save my changes
415
     * @When I try to save my changes
416
     */
417
    public function iSaveMyChanges()
418
    {
419
        $this->updatePage->saveChanges();
420
    }
421
422
    /**
423
     * @Then /^inventory of (this variant) should not be tracked$/
424
     */
425
    public function thisProductVariantShouldNotBeTracked(ProductVariantInterface $productVariant)
426
    {
427
        $this->iWantToModifyAProduct($productVariant);
428
429
        Assert::false($this->updatePage->isTracked());
430
    }
431
432
    /**
433
     * @Then /^inventory of (this variant) should be tracked$/
434
     */
435
    public function thisProductVariantShouldBeTracked(ProductVariantInterface $productVariant)
436
    {
437
        $this->iWantToModifyAProduct($productVariant);
438
439
        Assert::true($this->updatePage->isTracked());
440
    }
441
442
    /**
443
     * @When I generate it
444
     * @When I try to generate it
445
     */
446
    public function iClickGenerate()
447
    {
448
        $this->generatePage->generate();
449
    }
450
451
    /**
452
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant is identified by "([^"]+)" code and costs "(?:€|£|\$)([^"]+)" in ("[^"]+") channel$/
453
     */
454
    public function iSpecifyThereAreVariantsIdentifiedByCodeWithCost($nthVariant, $code, int $price, $channelName)
455
    {
456
        $this->generatePage->specifyCode($nthVariant - 1, $code);
457
        $this->generatePage->specifyPrice($nthVariant - 1, $price, $channelName);
458
    }
459
460
    /**
461
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant is identified by "([^"]+)" code$/
462
     */
463
    public function iSpecifyThereAreVariantsIdentifiedByCode($nthVariant, $code)
464
    {
465
        $this->generatePage->specifyCode($nthVariant - 1, $code);
466
    }
467
468
    /**
469
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant costs "(?:€|£|\$)([^"]+)" in ("[^"]+") channel$/
470
     */
471
    public function iSpecifyThereAreVariantsWithCost($nthVariant, int $price, $channelName)
472
    {
473
        $this->generatePage->specifyPrice($nthVariant - 1, $price, $channelName);
474
    }
475
476
    /**
477
     * @When /^I remove (\d)(?:st|nd|rd|th) variant from the list$/
478
     */
479
    public function iRemoveVariantFromTheList($nthVariant)
480
    {
481
        $this->generatePage->removeVariant($nthVariant - 1);
482
    }
483
484
    /**
485
     * @Then I should be notified that it has been successfully generated
486
     */
487
    public function iShouldBeNotifiedThatItHasBeenSuccessfullyGenerated()
488
    {
489
        $this->notificationChecker->checkNotification('Success Product variants have been successfully generated.', NotificationType::success());
490
    }
491
492
    /**
493
     * @Then I should not be able to generate any variants
494
     */
495
    public function iShouldNotBeAbleToGenerateAnyVariants(): void
496
    {
497
        Assert::false($this->generatePage->isGenerationPossible());
498
    }
499
500
    /**
501
     * @When I set its shipping category as :shippingCategoryName
502
     */
503
    public function iSetItsShippingCategoryAs($shippingCategoryName)
504
    {
505
        $this->createPage->selectShippingCategory($shippingCategoryName);
506
    }
507
508
    /**
509
     * @When I do not specify any information about variants
510
     */
511
    public function iDoNotSpecifyAnyInformationAboutVariants()
512
    {
513
        // Intentionally left blank to fulfill context expectation
514
    }
515
516
    /**
517
     * @When I change its quantity of inventory to :amount
518
     */
519
    public function iChangeItsQuantityOfInventoryTo(int $amount)
520
    {
521
        $this->updatePage->specifyCurrentStock($amount);
522
    }
523
524
    /**
525
     * @Then /^the (variant with code "[^"]+") should not have shipping required$/
526
     */
527
    public function theVariantWithCodeShouldNotHaveShippingRequired(ProductVariantInterface $productVariant)
528
    {
529
        $this->updatePage->open(['productId' => $productVariant->getProduct()->getId(), 'id' => $productVariant->getId()]);
530
531
        Assert::false($this->updatePage->isShippingRequired());
532
    }
533
534
    /**
535
     * @Then I should be notified that on hand quantity must be greater than the number of on hold units
536
     */
537
    public function iShouldBeNotifiedThatOnHandQuantityMustBeGreaterThanTheNumberOfOnHoldUnits()
538
    {
539
        Assert::same(
540
            $this->updatePage->getValidationMessage('on_hand'),
541
            'On hand must be greater than the number of on hold units'
542
        );
543
    }
544
545
    /**
546
     * @Then I should be notified that variants cannot be generated from options without any values
547
     */
548
    public function iShouldBeNotifiedThatVariantsCannotBeGeneratedFromOptionsWithoutAnyValues(): void
549
    {
550
        $this->notificationChecker->checkNotification('Cannot generate variants for a product without options values', NotificationType::failure());
551
    }
552
553
    /**
554
     * @Then I should not have configured price for :channelName channel
555
     */
556
    public function iShouldNotHaveConfiguredPriceForChannel(string $channelName): void
557
    {
558
        /** @var ProductVariantInterface $product */
559
        $productVariant = $this->sharedStorage->get('variant');
560
561
        $this->updatePage->open(['productId' => $productVariant->getProduct()->getId(), 'id' => $productVariant->getId()]);
562
563
        Assert::same($this->updatePage->getPriceForChannel($channelName), '');
564
    }
565
566
    /**
567
     * @Then I should see the :optionName option as :valueName
568
     */
569
    public function iShouldSeeTheOptionAs(string $optionName, string $valueName): void
570
    {
571
        Assert::true($this->updatePage->isSelectedOptionValueOnPage($optionName, $valueName));
572
    }
573
574
    /**
575
     * @When /^I want to generate new variants for (this product)$/
576
     * @When /^I try to generate new variants for (this product)$/
577
     */
578
    public function iTryToGenerateNewVariantsForThisProduct(ProductInterface $product): void
579
    {
580
        $this->generatePage->open(['productId' => $product->getId()]);
581
    }
582
583
    /**
584
     * @Then I should not be able to show this product in shop
585
     */
586
    public function iShouldNotBeAbleToShowThisProductInShop(): void
587
    {
588
        Assert::true($this->updatePage->isShowInShopButtonDisabled());
589
    }
590
591
    /**
592
     * @param string $element
593
     * @param string $message
594
     */
595
    private function assertValidationMessage($element, $message)
596
    {
597
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
598
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
599
600
        Assert::same($currentPage->getValidationMessage($element), $message);
601
    }
602
}
603