Completed
Push — master ( a026a7...7ff053 )
by Kamil
21s
created

iShouldBeNotifiedOfFailure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 1
eloc 4
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sylius\Behat\Context\Ui\Admin;
13
14
use Behat\Behat\Context\Context;
15
use Sylius\Behat\NotificationType;
16
use Sylius\Behat\Page\Admin\ProductVariant\CreatePageInterface;
17
use Sylius\Behat\Page\Admin\ProductVariant\GeneratePageInterface;
18
use Sylius\Behat\Page\Admin\ProductVariant\IndexPageInterface;
19
use Sylius\Behat\Page\Admin\ProductVariant\UpdatePageInterface;
20
use Sylius\Behat\Service\NotificationCheckerInterface;
21
use Sylius\Behat\Service\Resolver\CurrentPageResolverInterface;
22
use Sylius\Behat\Service\SharedStorageInterface;
23
use Sylius\Component\Core\Model\ProductInterface;
24
use Sylius\Component\Core\Model\ProductVariantInterface;
25
use Sylius\Component\Product\Resolver\ProductVariantResolverInterface;
26
use Webmozart\Assert\Assert;
27
28
/**
29
 * @author Łukasz Chruściel <[email protected]>
30
 * @author Gorka Laucirica <[email protected]>
31
 */
32
final class ManagingProductVariantsContext implements Context
33
{
34
    /**
35
     * @var SharedStorageInterface
36
     */
37
    private $sharedStorage;
38
39
    /**
40
     * @var ProductVariantResolverInterface
41
     */
42
    private $defaultProductVariantResolver;
43
44
    /**
45
     * @var CreatePageInterface
46
     */
47
    private $createPage;
48
49
    /**
50
     * @var IndexPageInterface
51
     */
52
    private $indexPage;
53
54
    /**
55
     * @var UpdatePageInterface
56
     */
57
    private $updatePage;
58
59
    /**
60
     * @var GeneratePageInterface
61
     */
62
    private $generatePage;
63
64
    /**
65
     * @var CurrentPageResolverInterface
66
     */
67
    private $currentPageResolver;
68
69
    /**
70
     * @var NotificationCheckerInterface
71
     */
72
    private $notificationChecker;
73
74
    /**
75
     * @param SharedStorageInterface $sharedStorage
76
     * @param ProductVariantResolverInterface $defaultProductVariantResolver
77
     * @param CreatePageInterface $createPage
78
     * @param IndexPageInterface $indexPage
79
     * @param UpdatePageInterface $updatePage
80
     * @param GeneratePageInterface $generatePage
81
     * @param CurrentPageResolverInterface $currentPageResolver
82
     * @param NotificationCheckerInterface $notificationChecker
83
     */
84
    public function __construct(
85
        SharedStorageInterface $sharedStorage,
86
        ProductVariantResolverInterface $defaultProductVariantResolver,
87
        CreatePageInterface $createPage,
88
        IndexPageInterface $indexPage,
89
        UpdatePageInterface $updatePage,
90
        GeneratePageInterface $generatePage,
91
        CurrentPageResolverInterface $currentPageResolver,
92
        NotificationCheckerInterface $notificationChecker
93
    ) {
94
        $this->sharedStorage = $sharedStorage;
95
        $this->defaultProductVariantResolver = $defaultProductVariantResolver;
96
        $this->createPage = $createPage;
97
        $this->indexPage = $indexPage;
98
        $this->updatePage = $updatePage;
99
        $this->generatePage = $generatePage;
100
        $this->currentPageResolver = $currentPageResolver;
101
        $this->notificationChecker = $notificationChecker;
102
    }
103
104
    /**
105
     * @Given /^I want to create a new variant of (this product)$/
106
     */
107
    public function iWantToCreateANewProduct(ProductInterface $product)
108
    {
109
        $this->createPage->open(['productId' => $product->getId()]);
110
    }
111
112
    /**
113
     * @When I specify its code as :code
114
     * @When I do not specify its code
115
     */
116
    public function iSpecifyItsCodeAs($code = null)
117
    {
118
        $this->createPage->specifyCode($code);
119
    }
120
121
    /**
122
     * @When I name it :name in :language
123
     */
124
    public function iNameItIn($name, $language)
125
    {
126
        $this->createPage->nameItIn($name, $language);
127
    }
128
129
    /**
130
     * @When I rename it to :name
131
     */
132
    public function iRenameItTo($name)
133
    {
134
        $this->updatePage->nameIt($name);
0 ignored issues
show
Bug introduced by
The method nameIt() does not seem to exist on object<Sylius\Behat\Page...nt\UpdatePageInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
135
    }
136
137
    /**
138
     * @When I add it
139
     * @When I try to add it
140
     */
141
    public function iAddIt()
142
    {
143
        $this->createPage->create();
144
    }
145
146
    /**
147
     * @When I disable its inventory tracking
148
     */
149
    public function iDisableItsTracking()
150
    {
151
        $this->updatePage->disableTracking();
152
    }
153
154
    /**
155
     * @When I enable its inventory tracking
156
     */
157
    public function iEnableItsTracking()
158
    {
159
        $this->updatePage->enableTracking();
160
    }
161
162
    /**
163
     * @When /^I set its(?:| default) price to "(?:€|£|\$)([^"]+)" for "([^"]+)" channel$/
164
     * @When I do not set its price
165
     */
166
    public function iSetItsPriceTo($price = null, $channelName = null)
167
    {
168
        $this->createPage->specifyPrice($price, (null === $channelName) ? $this->sharedStorage->get('channel') :$channelName);
169
    }
170
171
    /**
172
     * @When /^I set its original price to "(?:€|£|\$)([^"]+)" for "([^"]+)" channel$/
173
     */
174
    public function iSetItsOriginalPriceTo($originalPrice, $channelName)
175
    {
176
        $this->createPage->specifyOriginalPrice($originalPrice, $channelName);
177
    }
178
179
    /**
180
     * @When I set its height, width, depth and weight to :number
181
     */
182
    public function iSetItsDimensionsTo($value)
183
    {
184
        $this->createPage->specifyHeightWidthDepthAndWeight($value, $value, $value, $value);
185
    }
186
187
    /**
188
     * @When I do not specify its current stock
189
     */
190
    public function iDoNetSetItsCurrentStockTo()
191
    {
192
        $this->createPage->specifyCurrentStock('');
193
    }
194
195
    /**
196
     * @When I choose :calculatorName calculator
197
     */
198
    public function iChooseCalculator($calculatorName)
199
    {
200
        $this->createPage->choosePricingCalculator($calculatorName);
201
    }
202
203
    /**
204
     * @When I set its :optionName option to :optionValue
205
     */
206
    public function iSetItsOptionAs($optionName, $optionValue)
207
    {
208
        $this->createPage->selectOption($optionName, $optionValue);
209
    }
210
211
    /**
212
     * @When I start sorting variants by :field
213
     */
214
    public function iSortProductsBy($field)
215
    {
216
        $this->indexPage->sortBy($field);
217
    }
218
219
    /**
220
     * @When I set the position of :name to :position
221
     */
222
    public function iSetThePositionOfTo($name, $position)
223
    {
224
        $this->indexPage->setPosition($name, (int) $position);
225
    }
226
227
    /**
228
     * @When I save my new configuration
229
     */
230
    public function iSaveMyNewConfiguration()
231
    {
232
        $this->indexPage->savePositions();
233
    }
234
235
    /**
236
     * @Then the :productVariantCode variant of the :product product should appear in the store
237
     */
238
    public function theProductVariantShouldAppearInTheShop($productVariantCode, ProductInterface $product)
239
    {
240
        $this->iWantToViewAllVariantsOfThisProduct($product);
241
242
        Assert::true($this->indexPage->isSingleResourceOnPage(['code' => $productVariantCode]));
243
    }
244
245
    /**
246
     * @Then the :productVariantCode variant of the :product product should not appear in the store
247
     */
248
    public function theProductVariantShouldNotAppearInTheShop($productVariantCode, ProductInterface $product)
249
    {
250
        $this->iWantToViewAllVariantsOfThisProduct($product);
251
252
        Assert::false($this->indexPage->isSingleResourceOnPage(['code' => $productVariantCode]));
253
    }
254
255
    /**
256
     * @Then the :product product should have no variants
257
     */
258
    public function theProductShouldHaveNoVariants(ProductInterface $product)
259
    {
260
        $this->assertNumberOfVariantsOnProductPage($product, 0);
261
    }
262
263
    /**
264
     * @Then the :product product should have only one variant
265
     */
266
    public function theProductShouldHaveOnlyOneVariant(ProductInterface $product)
267
    {
268
        $this->assertNumberOfVariantsOnProductPage($product, 1);
269
    }
270
271
    /**
272
     * @Then /^the (variant with code "[^"]+") should be priced at (?:€|£|\$)([^"]+) for channel "([^"]+)"$/
273
     */
274
    public function theVariantWithCodeShouldBePricedAtForChannel(ProductVariantInterface $productVariant, $price, $channelName)
275
    {
276
        $this->updatePage->open(['id' => $productVariant->getId(), 'productId' => $productVariant->getProduct()->getId()]);
277
278
        Assert::same($this->updatePage->getPriceForChannel($channelName), $price);
279
    }
280
281
    /**
282
     * @Then /^the (variant with code "[^"]+") should be named "([^"]+)" in ("([^"]+)" locale)$/
283
     */
284
    public function theVariantWithCodeShouldBeNamedIn(ProductVariantInterface $productVariant, $name, $language)
285
    {
286
        $this->updatePage->open(['id' => $productVariant->getId(), 'productId' => $productVariant->getProduct()->getId()]);
287
288
        Assert::same($this->updatePage->getNameInLanguage($language), $name);
289
    }
290
291
    /**
292
     * @Then /^the (variant with code "[^"]+") should have an original price of (?:€|£|\$)([^"]+) for channel "([^"]+)"$/
293
     */
294
    public function theVariantWithCodeShouldHaveAnOriginalPriceOfForChannel(ProductVariantInterface $productVariant, $originalPrice, $channelName)
295
    {
296
        $this->updatePage->open(['id' => $productVariant->getId(), 'productId' => $productVariant->getProduct()->getId()]);
297
298
        Assert::same(
299
            $this->updatePage->getOriginalPriceForChannel($channelName),
300
            $originalPrice
301
        );
302
    }
303
304
    /**
305
     * @When /^I (?:|want to )view all variants of (this product)$/
306
     * @When /^I view(?:| all) variants of the (product "[^"]+")$/
307
     */
308
    public function iWantToViewAllVariantsOfThisProduct(ProductInterface $product)
309
    {
310
        $this->indexPage->open(['productId' => $product->getId()]);
311
    }
312
313
    /**
314
     * @Then I should see :numberOfProductVariants variants in the list
315
     * @Then I should see :numberOfProductVariants variant in the list
316
     * @Then I should not see any variants in the list
317
     */
318
    public function iShouldSeeProductVariantsInTheList($numberOfProductVariants = 0)
319
    {
320
        Assert::same($this->indexPage->countItems(), (int) $numberOfProductVariants);
321
    }
322
323
    /**
324
     * @When /^I delete the ("[^"]+" variant of product "[^"]+")$/
325
     * @When /^I try to delete the ("[^"]+" variant of product "[^"]+")$/
326
     */
327
    public function iDeleteTheVariantOfProduct(ProductVariantInterface $productVariant)
328
    {
329
        $this->iWantToViewAllVariantsOfThisProduct($productVariant->getProduct());
0 ignored issues
show
Compatibility introduced by
$productVariant->getProduct() of type object<Sylius\Component\...Model\ProductInterface> is not a sub-type of object<Sylius\Component\...Model\ProductInterface>. It seems like you assume a child interface of the interface Sylius\Component\Product\Model\ProductInterface to be always present.

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

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

Loading history...
330
331
        $this->indexPage->deleteResourceOnPage(['code' => $productVariant->getCode()]);
332
    }
333
334
    /**
335
     * @Then /^(this variant) should not exist in the product catalog$/
336
     */
337
    public function productVariantShouldNotExist(ProductVariantInterface $productVariant)
338
    {
339
        $this->iWantToViewAllVariantsOfThisProduct($productVariant->getProduct());
0 ignored issues
show
Compatibility introduced by
$productVariant->getProduct() of type object<Sylius\Component\...Model\ProductInterface> is not a sub-type of object<Sylius\Component\...Model\ProductInterface>. It seems like you assume a child interface of the interface Sylius\Component\Product\Model\ProductInterface to be always present.

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

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

Loading history...
340
341
        Assert::false($this->indexPage->isSingleResourceOnPage(['name' => $productVariant->getName()]));
342
    }
343
344
    /**
345
     * @Then I should be notified that this variant is in use and cannot be deleted
346
     */
347
    public function iShouldBeNotifiedOfFailure()
348
    {
349
        $this->notificationChecker->checkNotification(
350
            'Cannot delete, the product variant is in use.',
351
            NotificationType::failure()
352
        );
353
    }
354
355
    /**
356
     * @Then /^(this variant) should still exist in the product catalog$/
357
     */
358
    public function productShouldExistInTheProductCatalog(ProductVariantInterface $productVariant)
359
    {
360
        $this->theProductVariantShouldAppearInTheShop($productVariant->getCode(), $productVariant->getProduct());
0 ignored issues
show
Compatibility introduced by
$productVariant->getProduct() of type object<Sylius\Component\...Model\ProductInterface> is not a sub-type of object<Sylius\Component\...Model\ProductInterface>. It seems like you assume a child interface of the interface Sylius\Component\Product\Model\ProductInterface to be always present.

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

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

Loading history...
361
    }
362
363
    /**
364
     * @When /^I want to modify the ("[^"]+" product variant)$/
365
     */
366
    public function iWantToModifyAProduct(ProductVariantInterface $productVariant)
367
    {
368
        $this->updatePage->open(['id' => $productVariant->getId(), 'productId' => $productVariant->getProduct()->getId()]);
369
    }
370
371
    /**
372
     * @Then the code field should be disabled
373
     */
374
    public function theCodeFieldShouldBeDisabled()
375
    {
376
        Assert::true($this->updatePage->isCodeDisabled());
377
    }
378
379
    /**
380
     * @Then I should be notified that :element is required
381
     */
382
    public function iShouldBeNotifiedThatIsRequired($element)
383
    {
384
        $this->assertValidationMessage($element, sprintf('Please enter the %s.', $element));
385
    }
386
387
    /**
388
     * @Then I should be notified that code has to be unique
389
     */
390
    public function iShouldBeNotifiedThatCodeHasToBeUnique()
391
    {
392
        $this->assertValidationMessage('code', 'Product variant code must be unique.');
393
    }
394
395
    /**
396
     * @Then I should be notified that current stock is required
397
     */
398
    public function iShouldBeNotifiedThatOnHandIsRequired()
399
    {
400
        $this->assertValidationMessage('on_hand', 'Please enter on hand.');
401
    }
402
403
    /**
404
     * @Then I should be notified that height, width, depth and weight cannot be lower than 0
405
     */
406
    public function iShouldBeNotifiedThatIsHeightWidthDepthWeightCannotBeLowerThan()
407
    {
408
        $this->assertValidationMessage('height', 'Height cannot be negative.');
409
        $this->assertValidationMessage('width', 'Width cannot be negative.');
410
        $this->assertValidationMessage('depth', 'Depth cannot be negative.');
411
        $this->assertValidationMessage('weight', 'Weight cannot be negative.');
412
    }
413
414
    /**
415
     * @Then I should be notified that price cannot be lower than 0.01
416
     */
417
    public function iShouldBeNotifiedThatPriceCannotBeLowerThen()
418
    {
419
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
420
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
421
422
        Assert::contains($currentPage->getPricesValidationMessage(), 'Price must be at least 0.01.');
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...
423
    }
424
425
    /**
426
     * @Then I should be notified that this variant already exists
427
     */
428
    public function iShouldBeNotifiedThatThisVariantAlreadyExists()
429
    {
430
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
431
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
432
433
        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...
434
    }
435
436
    /**
437
     * @Then /^I should be notified that code is required for the (\d)(?:st|nd|rd|th) variant$/
438
     */
439
    public function iShouldBeNotifiedThatCodeIsRequiredForVariant($position)
440
    {
441
        Assert::same(
442
            $this->generatePage->getValidationMessage('code', $position - 1),
443
            'Please enter the code.'
444
        );
445
    }
446
447
    /**
448
     * @Then /^I should be notified that prices in all channels must be defined for the (\d)(?:st|nd|rd|th) variant$/
449
     */
450
    public function iShouldBeNotifiedThatPricesInAllChannelsMustBeDefinedForTheVariant($position)
451
    {
452
        Assert::same(
453
            $this->generatePage->getPricesValidationMessage($position - 1),
454
            'You must define price for every channel.'
455
        );
456
    }
457
458
    /**
459
     * @Then /^I should be notified that variant code must be unique within this product for the (\d)(?:st|nd|rd|th) variant$/
460
     */
461
    public function iShouldBeNotifiedThatVariantCodeMustBeUniqueWithinThisProductForYheVariant($position)
462
    {
463
        Assert::same(
464
            $this->generatePage->getValidationMessage('code', $position - 1),
465
            'This code must be unique within this product.'
466
        );
467
    }
468
469
    /**
470
     * @Then I should be notified that prices in all channels must be defined
471
     */
472
    public function iShouldBeNotifiedThatPricesInAllChannelsMustBeDefined()
473
    {
474
        Assert::contains(
475
            $this->createPage->getPricesValidationMessage(),
476
            'You must define price for every channel.'
477
        );
478
    }
479
480
    /**
481
     * @When I save my changes
482
     * @When I try to save my changes
483
     */
484
    public function iSaveMyChanges()
485
    {
486
        $this->updatePage->saveChanges();
487
    }
488
489
    /**
490
     * @When I remove its name
491
     */
492
    public function iRemoveItsNameFromTranslation()
493
    {
494
        $this->updatePage->nameIt('');
0 ignored issues
show
Bug introduced by
The method nameIt() does not seem to exist on object<Sylius\Behat\Page...nt\UpdatePageInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
495
    }
496
497
    /**
498
     * @Then /^the variant "([^"]+)" should have (\d+) items on hand$/
499
     */
500
    public function thisVariantShouldHaveItemsOnHand($productVariantName, $quantity)
501
    {
502
        Assert::true($this->indexPage->isSingleResourceWithSpecificElementOnPage(
503
            ['name' => $productVariantName],
504
            sprintf('td > div.ui.label:contains("%s")', $quantity)
505
        ));
506
    }
507
508
    /**
509
     * @Then /^the "([^"]+)" variant of ("[^"]+" product) should have (\d+) items on hand$/
510
     */
511
    public function theVariantOfProductShouldHaveItemsOnHand($productVariantName, ProductInterface $product, $quantity)
512
    {
513
        $this->indexPage->open(['productId' => $product->getId()]);
514
515
        Assert::true($this->indexPage->isSingleResourceWithSpecificElementOnPage(
516
            ['name' => $productVariantName],
517
            sprintf('td > div.ui.label:contains("%s")', $quantity)
518
        ));
519
    }
520
521
    /**
522
     * @Then /^inventory of (this variant) should not be tracked$/
523
     */
524
    public function thisProductVariantShouldNotBeTracked(ProductVariantInterface $productVariant)
525
    {
526
        $this->iWantToModifyAProduct($productVariant);
527
528
        Assert::false($this->updatePage->isTracked());
529
    }
530
531
    /**
532
     * @Then /^inventory of (this variant) should be tracked$/
533
     */
534
    public function thisProductVariantShouldBeTracked(ProductVariantInterface $productVariant)
535
    {
536
        $this->iWantToModifyAProduct($productVariant);
537
538
        Assert::true($this->updatePage->isTracked());
539
    }
540
541
    /**
542
     * @Then /^I should see that the ("([^"]+)" variant) is not tracked$/
543
     */
544
    public function iShouldSeeThatIsNotTracked(ProductVariantInterface $productVariant)
545
    {
546
        Assert::true($this->indexPage->isSingleResourceOnPage([
547
            'name' => $productVariant->getName(),
548
            'inventory' => 'Not tracked',
549
        ]));
550
    }
551
552
    /**
553
     * @Then /^I should see that the ("[^"]+" variant) has zero on hand quantity$/
554
     */
555
    public function iShouldSeeThatTheVariantHasZeroOnHandQuantity(ProductVariantInterface $productVariant)
556
    {
557
        Assert::true($this->indexPage->isSingleResourceOnPage([
558
            'name' => $productVariant->getName(),
559
            'inventory' => '0 Available on hand',
560
        ]));
561
    }
562
563
    /**
564
     * @Then /^(\d+) units of (this product) should be on hold$/
565
     */
566
    public function unitsOfThisProductShouldBeOnHold($quantity, ProductInterface $product)
567
    {
568
        /** @var ProductVariantInterface $variant */
569
        $variant = $this->defaultProductVariantResolver->getVariant($product);
570
571
        $this->assertOnHoldQuantityOfVariant($quantity, $variant);
572
    }
573
574
    /**
575
     * @Then /^(\d+) units of (this product) should be on hand$/
576
     */
577
    public function unitsOfThisProductShouldBeOnHand($quantity, ProductInterface $product)
578
    {
579
        /** @var ProductVariantInterface $variant */
580
        $variant = $this->defaultProductVariantResolver->getVariant($product);
581
582
        Assert::same($this->indexPage->getOnHandQuantityFor($variant), (int) $quantity);
583
    }
584
585
    /**
586
     * @Then /^there should be no units of (this product) on hold$/
587
     */
588
    public function thereShouldBeNoUnitsOfThisProductOnHold(ProductInterface $product)
589
    {
590
        /** @var ProductVariantInterface $variant */
591
        $variant = $this->defaultProductVariantResolver->getVariant($product);
592
593
        $this->assertOnHoldQuantityOfVariant(0, $variant);
594
    }
595
596
    /**
597
     * @Then the :variant variant should have :amount items on hold
598
     */
599
    public function thisVariantShouldHaveItemsOnHold(ProductVariantInterface $variant, $amount)
600
    {
601
        $this->assertOnHoldQuantityOfVariant((int) $amount, $variant);
602
    }
603
604
    /**
605
     * @Then the :variant variant of :product product should have :amount items on hold
606
     */
607
    public function theVariantOfProductShouldHaveItemsOnHold(ProductVariantInterface $variant, ProductInterface $product, $amount)
608
    {
609
        $this->indexPage->open(['productId' => $product->getId()]);
610
611
        $this->assertOnHoldQuantityOfVariant((int) $amount, $variant);
612
    }
613
614
    /**
615
     * @Then the first variant in the list should have :field :value
616
     */
617
    public function theFirstVariantInTheListShouldHave($field, $value)
618
    {
619
        Assert::same($this->indexPage->getColumnFields($field)[0], $value);
620
    }
621
622
    /**
623
     * @Then the last variant in the list should have :field :value
624
     */
625
    public function theLastVariantInTheListShouldHave($field, $value)
626
    {
627
        $values = $this->indexPage->getColumnFields($field);
628
629
        Assert::same(end($values), $value);
630
    }
631
632
    /**
633
     * @When /^I want to generate new variants for (this product)$/
634
     */
635
    public function iWantToGenerateNewVariantsForThisProduct(ProductInterface $product)
636
    {
637
        $this->generatePage->open(['productId' => $product->getId()]);
638
    }
639
640
    /**
641
     * @When I generate it
642
     * @When I try to generate it
643
     */
644
    public function iClickGenerate()
645
    {
646
        $this->generatePage->generate();
647
    }
648
649
    /**
650
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant is identified by "([^"]+)" code and costs "(?:€|£|\$)([^"]+)" in ("[^"]+") channel$/
651
     */
652
    public function iSpecifyThereAreVariantsIdentifiedByCodeWithCost($nthVariant, $code, $price, $channelName)
653
    {
654
        $this->generatePage->specifyCode($nthVariant - 1, $code);
655
        $this->generatePage->specifyPrice($nthVariant - 1, $price, $channelName);
656
    }
657
658
    /**
659
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant is identified by "([^"]+)" code$/
660
     */
661
    public function iSpecifyThereAreVariantsIdentifiedByCode($nthVariant, $code)
662
    {
663
        $this->generatePage->specifyCode($nthVariant - 1, $code);
664
    }
665
666
    /**
667
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant costs "(?:€|£|\$)([^"]+)" in ("[^"]+") channel$/
668
     */
669
    public function iSpecifyThereAreVariantsWithCost($nthVariant, $price, $channelName)
670
    {
671
        $this->generatePage->specifyPrice($nthVariant - 1, $price, $channelName);
672
    }
673
674
    /**
675
     * @When /^I remove (\d)(?:st|nd|rd|th) variant from the list$/
676
     */
677
    public function iRemoveVariantFromTheList($nthVariant)
678
    {
679
        $this->generatePage->removeVariant($nthVariant - 1);
680
    }
681
682
    /**
683
     * @Then I should be notified that it has been successfully generated
684
     */
685
    public function iShouldBeNotifiedThatItHasBeenSuccessfullyGenerated()
686
    {
687
        $this->notificationChecker->checkNotification('Success Product variants have been successfully generated.', NotificationType::success());
688
    }
689
690
    /**
691
     * @When I set its shipping category as :shippingCategoryName
692
     */
693
    public function iSetItsShippingCategoryAs($shippingCategoryName)
694
    {
695
        $this->createPage->selectShippingCategory($shippingCategoryName);
696
    }
697
698
    /**
699
     * @When I do not specify any information about variants
700
     */
701
    public function iDoNotSpecifyAnyInformationAboutVariants()
702
    {
703
        // Intentionally left blank to fulfill context expectation
704
    }
705
706
    /**
707
     * @When I change its quantity of inventory to :amount
708
     */
709
    public function iChangeItsQuantityOfInventoryTo($amount)
710
    {
711
        $this->updatePage->specifyCurrentStock($amount);
712
    }
713
714
    /**
715
     * @Then /^(this variant) should have a (\d+) item currently in stock$/
716
     */
717
    public function thisVariantShouldHaveAItemCurrentlyInStock(ProductVariantInterface $productVariant, $amountInStock)
718
    {
719
        $this->indexPage->open(['productId' => $productVariant->getProduct()->getId()]);
720
721
        Assert::same($this->indexPage->getOnHandQuantityFor($productVariant), (int) $amountInStock);
722
    }
723
724
    /**
725
     * @Then I should be notified that on hand quantity must be greater than the number of on hold units
726
     */
727
    public function iShouldBeNotifiedThatOnHandQuantityMustBeGreaterThanTheNumberOfOnHoldUnits()
728
    {
729
        Assert::same(
730
            $this->updatePage->getValidationMessage('on_hand'),
731
            'On hand must be greater than the number of on hold units'
732
        );
733
    }
734
735
    /**
736
     * @param string $element
737
     * @param $message
738
     */
739
    private function assertValidationMessage($element, $message)
740
    {
741
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
742
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
743
744
        Assert::same($currentPage->getValidationMessage($element), $message);
745
    }
746
747
    /**
748
     * @param int $expectedAmount
749
     * @param ProductVariantInterface $variant
750
     *
751
     * @throws \InvalidArgumentException
752
     */
753
    private function assertOnHoldQuantityOfVariant($expectedAmount, $variant)
754
    {
755
        $actualAmount = $this->indexPage->getOnHoldQuantityFor($variant);
756
757
        Assert::same(
758
            $actualAmount,
759
            (int) $expectedAmount,
760
            sprintf(
761
                'Unexpected on hold quantity for "%s" variant. It should be "%s" but is "%s"',
762
                $variant->getName(),
763
                $expectedAmount,
764
                $actualAmount
765
            )
766
        );
767
    }
768
769
    /**
770
     * @param ProductInterface $product
771
     * @param int $amount
772
     */
773
    private function assertNumberOfVariantsOnProductPage(ProductInterface $product, $amount)
774
    {
775
        $this->iWantToViewAllVariantsOfThisProduct($product);
776
777
        Assert::same((int) $this->indexPage->countItems(), $amount, 'Product has %d variants, but should have %d');
778
    }
779
}
780