Completed
Push — master ( 7d844f...319af7 )
by Kamil
17s
created

thereShouldBeNoUnitsOfThisProductOnHold()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
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\DefaultProductVariantResolver;
26
use Webmozart\Assert\Assert;
27
28
/**
29
 * @author Łukasz Chruściel <[email protected]>
30
 */
31
final class ManagingProductVariantsContext implements Context
32
{
33
    /**
34
     * @var SharedStorageInterface
35
     */
36
    private $sharedStorage;
37
38
    /**
39
     * @var DefaultProductVariantResolver
40
     */
41
    private $defaultProductVariantResolver;
42
43
    /**
44
     * @var CreatePageInterface
45
     */
46
    private $createPage;
47
48
    /**
49
     * @var IndexPageInterface
50
     */
51
    private $indexPage;
52
53
    /**
54
     * @var UpdatePageInterface
55
     */
56
    private $updatePage;
57
58
    /**
59
     * @var GeneratePageInterface
60
     */
61
    private $generatePage;
62
63
    /**
64
     * @var CurrentPageResolverInterface
65
     */
66
    private $currentPageResolver;
67
68
    /**
69
     * @var NotificationCheckerInterface
70
     */
71
    private $notificationChecker;
72
73
    /**
74
     * @param SharedStorageInterface $sharedStorage
75
     * @param DefaultProductVariantResolver $defaultProductVariantResolver
76
     * @param CreatePageInterface $createPage
77
     * @param IndexPageInterface $indexPage
78
     * @param UpdatePageInterface $updatePage
79
     * @param GeneratePageInterface $generatePage
80
     * @param CurrentPageResolverInterface $currentPageResolver
81
     * @param NotificationCheckerInterface $notificationChecker
82
     */
83
    public function __construct(
84
        SharedStorageInterface $sharedStorage,
85
        DefaultProductVariantResolver $defaultProductVariantResolver,
86
        CreatePageInterface $createPage,
87
        IndexPageInterface $indexPage,
88
        UpdatePageInterface $updatePage,
89
        GeneratePageInterface $generatePage,
90
        CurrentPageResolverInterface $currentPageResolver,
91
        NotificationCheckerInterface $notificationChecker
92
    ) {
93
        $this->sharedStorage = $sharedStorage;
94
        $this->defaultProductVariantResolver = $defaultProductVariantResolver;
95
        $this->createPage = $createPage;
96
        $this->indexPage = $indexPage;
97
        $this->updatePage = $updatePage;
98
        $this->generatePage = $generatePage;
99
        $this->currentPageResolver = $currentPageResolver;
100
        $this->notificationChecker = $notificationChecker;
101
    }
102
103
    /**
104
     * @Given /^I want to create a new variant of (this product)$/
105
     */
106
    public function iWantToCreateANewProduct(ProductInterface $product)
107
    {
108
        $this->createPage->open(['productId' => $product->getId()]);
109
    }
110
111
    /**
112
     * @When I specify its code as :code
113
     * @When I do not specify its code
114
     */
115
    public function iSpecifyItsCodeAs($code = null)
116
    {
117
        $this->createPage->specifyCode($code);
118
    }
119
120
    /**
121
     * @When I name it :name in :language
122
     */
123
    public function iNameItIn($name, $language)
124
    {
125
        $this->createPage->nameItIn($name, $language);
126
    }
127
128
    /**
129
     * @When I rename it to :name
130
     */
131
    public function iRenameItTo($name)
132
    {
133
        $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...
134
    }
135
136
    /**
137
     * @When I add it
138
     * @When I try to add it
139
     */
140
    public function iAddIt()
141
    {
142
        $this->createPage->create();
143
    }
144
145
    /**
146
     * @When I disable its inventory tracking
147
     */
148
    public function iDisableItsTracking()
149
    {
150
        $this->updatePage->disableTracking();
151
    }
152
153
    /**
154
     * @When I enable its inventory tracking
155
     */
156
    public function iEnableItsTracking()
157
    {
158
        $this->updatePage->enableTracking();
159
    }
160
161
    /**
162
     * @When /^I set its(?:| default) price to "(?:€|£|\$)([^"]+)" for "([^"]+)" channel$/
163
     * @When I do not set its price
164
     */
165
    public function iSetItsPriceTo($price = null, $channel = null)
166
    {
167
        $this->createPage->specifyPrice($price, (null === $channel) ? $this->sharedStorage->get('channel') :$channel);
168
    }
169
170
    /**
171
     * @When I set its height, width, depth and weight to :number
172
     */
173
    public function iSetItsDimensionsTo($value)
174
    {
175
        $this->createPage->specifyHeightWidthDepthAndWeight($value, $value, $value, $value);
176
    }
177
178
    /**
179
     * @When I do not specify its current stock
180
     */
181
    public function iDoNetSetItsCurrentStockTo()
182
    {
183
        $this->createPage->specifyCurrentStock('');
184
    }
185
186
    /**
187
     * @When I choose :calculatorName calculator
188
     */
189
    public function iChooseCalculator($calculatorName)
190
    {
191
        $this->createPage->choosePricingCalculator($calculatorName);
192
    }
193
194
    /**
195
     * @When I set its :optionName option to :optionValue
196
     */
197
    public function iSetItsOptionAs($optionName, $optionValue)
198
    {
199
        $this->createPage->selectOption($optionName, $optionValue);
200
    }
201
202
    /**
203
     * @When I start sorting variants by :field
204
     */
205
    public function iSortProductsBy($field)
206
    {
207
        $this->indexPage->sortBy($field);
208
    }
209
210
    /**
211
     * @When I set the position of :name to :position
212
     */
213
    public function iSetThePositionOfTo($name, $position)
214
    {
215
        $this->indexPage->setPosition($name, (int) $position);
216
    }
217
218
    /**
219
     * @When I save my new configuration
220
     */
221
    public function iSaveMyNewConfiguration()
222
    {
223
        $this->indexPage->savePositions();
224
    }
225
226
    /**
227
     * @Then the :productVariantCode variant of the :product product should appear in the store
228
     */
229
    public function theProductVariantShouldAppearInTheShop($productVariantCode, ProductInterface $product)
230
    {
231
        $this->iWantToViewAllVariantsOfThisProduct($product);
232
233
        Assert::true(
234
            $this->indexPage->isSingleResourceOnPage(['code' => $productVariantCode]),
235
            sprintf('The product variant with code %s has not been found.', $productVariantCode)
236
        );
237
    }
238
239
    /**
240
     * @Then the :productVariantCode variant of the :product product should not appear in the store
241
     */
242
    public function theProductVariantShouldNotAppearInTheShop($productVariantCode, ProductInterface $product)
243
    {
244
        $this->iWantToViewAllVariantsOfThisProduct($product);
245
246
        Assert::false(
247
            $this->indexPage->isSingleResourceOnPage(['code' => $productVariantCode]),
248
            sprintf('The product variant with code %s has not been found.', $productVariantCode)
249
        );
250
    }
251
252
    /**
253
     * @Then the :product product should have no variants
254
     */
255
    public function theProductShouldHaveNoVariants(ProductInterface $product)
256
    {
257
        $this->assertNumberOfVariantsOnProductPage($product, 0);
258
    }
259
260
    /**
261
     * @Then the :product product should have only one variant
262
     */
263
    public function theProductShouldHaveOnlyOneVariant(ProductInterface $product)
264
    {
265
        $this->assertNumberOfVariantsOnProductPage($product, 1);
266
    }
267
268
    /**
269
     * @Then /^the (variant with code "[^"]+") should be priced at (?:€|£|\$)([^"]+) for channel "([^"]+)"$/
270
     */
271
    public function theVariantWithCodeShouldBePricedAtForChannel(ProductVariantInterface $productVariant, $price, $channelName)
272
    {
273
        $this->updatePage->open(['id' => $productVariant->getId(), 'productId' => $productVariant->getProduct()->getId()]);
274
275
        Assert::same(
276
            $this->updatePage->getPriceForChannel($channelName),
277
            $price
278
        );
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($name, $this->updatePage->getNameInLanguage($language));
289
    }
290
291
    /**
292
     * @When /^I (?:|want to )view all variants of (this product)$/
293
     * @When /^I view(?:| all) variants of the (product "[^"]+")$/
294
     */
295
    public function iWantToViewAllVariantsOfThisProduct(ProductInterface $product)
296
    {
297
        $this->indexPage->open(['productId' => $product->getId()]);
298
    }
299
300
    /**
301
     * @Then I should see :numberOfProductVariants variants in the list
302
     * @Then I should see :numberOfProductVariants variant in the list
303
     * @Then I should not see any variants in the list
304
     */
305
    public function iShouldSeeProductVariantsInTheList($numberOfProductVariants = 0)
306
    {
307
        $foundRows = $this->indexPage->countItems();
308
309
        Assert::same(
310
            (int) $numberOfProductVariants,
311
            $foundRows,
312
            '%s rows with product variants should appear on page, %s rows has been found'
313
        );
314
    }
315
316
    /**
317
     * @When /^I delete the ("[^"]+" variant of product "[^"]+")$/
318
     * @When /^I try to delete the ("[^"]+" variant of product "[^"]+")$/
319
     */
320
    public function iDeleteTheVariantOfProduct(ProductVariantInterface $productVariant)
321
    {
322
        $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...
323
324
        $this->indexPage->deleteResourceOnPage(['code' => $productVariant->getCode()]);
325
    }
326
327
    /**
328
     * @Then /^(this variant) should not exist in the product catalog$/
329
     */
330
    public function productVariantShouldNotExist(ProductVariantInterface $productVariant)
331
    {
332
        $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...
333
334
        Assert::false(
335
            $this->indexPage->isSingleResourceOnPage(['name' => $productVariant->getName()]),
336
            sprintf('Product variant with code %s exists but should not.', $productVariant->getName())
337
        );
338
    }
339
340
    /**
341
     * @Then I should be notified that this variant is in use and cannot be deleted
342
     */
343
    public function iShouldBeNotifiedOfFailure()
344
    {
345
        $this->notificationChecker->checkNotification(
346
            'Cannot delete, the product variant is in use.',
347
            NotificationType::failure()
348
        );
349
    }
350
351
    /**
352
     * @Then /^(this variant) should still exist in the product catalog$/
353
     */
354
    public function productShouldExistInTheProductCatalog(ProductVariantInterface $productVariant)
355
    {
356
        $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...
357
    }
358
359
    /**
360
     * @When /^I want to modify the ("[^"]+" product variant)$/
361
     */
362
    public function iWantToModifyAProduct(ProductVariantInterface $productVariant)
363
    {
364
        $this->updatePage->open(['id' => $productVariant->getId(), 'productId' => $productVariant->getProduct()->getId()]);
365
    }
366
367
    /**
368
     * @Then the code field should be disabled
369
     */
370
    public function theCodeFieldShouldBeDisabled()
371
    {
372
        Assert::true(
373
            $this->updatePage->isCodeDisabled(),
374
            'Code should be immutable, but it does not.'
375
        );
376
    }
377
378
    /**
379
     * @Then I should be notified that :element is required
380
     */
381
    public function iShouldBeNotifiedThatIsRequired($element)
382
    {
383
        $this->assertValidationMessage($element, sprintf('Please enter the %s.', $element));
384
    }
385
386
    /**
387
     * @Then I should be notified that code has to be unique
388
     */
389
    public function iShouldBeNotifiedThatCodeHasToBeUnique()
390
    {
391
        $this->assertValidationMessage('code', 'Product variant code must be unique.');
392
    }
393
394
    /**
395
     * @Then I should be notified that current stock is required
396
     */
397
    public function iShouldBeNotifiedThatOnHandIsRequired()
398
    {
399
        $this->assertValidationMessage('on_hand', 'Please enter on hand.');
400
    }
401
402
    /**
403
     * @Then I should be notified that height, width, depth and weight cannot be lower than 0
404
     */
405
    public function iShouldBeNotifiedThatIsHeightWidthDepthWeightCannotBeLowerThan()
406
    {
407
        $this->assertValidationMessage('height', 'Height cannot be negative.');
408
        $this->assertValidationMessage('width', 'Width cannot be negative.');
409
        $this->assertValidationMessage('depth', 'Depth cannot be negative.');
410
        $this->assertValidationMessage('weight', 'Weight cannot be negative.');
411
    }
412
413
    /**
414
     * @Then I should be notified that price cannot be lower than 0.01
415
     */
416
    public function iShouldBeNotifiedThatPriceCannotBeLowerThen()
417
    {
418
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
419
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
420
421
        Assert::same($currentPage->getFirstPriceValidationMessage(), 'Price must be at least 0.01.');
0 ignored issues
show
Bug introduced by
The method getFirstPriceValidationMessage 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...
422
    }
423
424
    /**
425
     * @Then I should be notified that this variant already exists
426
     */
427
    public function iShouldBeNotifiedThatThisVariantAlreadyExists()
428
    {
429
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
430
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
431
432
        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...
433
    }
434
435
    /**
436
     * @Then /^I should be notified that code is required for the (\d)(?:st|nd|rd|th) variant$/
437
     */
438
    public function iShouldBeNotifiedThatCodeIsRequiredForVariant($position)
439
    {
440
        Assert::same(
441
            $this->generatePage->getValidationMessage('code', $position - 1),
442
            'Please enter the code.'
443
        );
444
    }
445
446
    /**
447
     * @Then /^I should be notified that prices in all channels must be defined for the (\d)(?:st|nd|rd|th) variant$/
448
     */
449
    public function iShouldBeNotifiedThatPricesInAllChannelsMustBeDefinedForTheVariant($position)
450
    {
451
        Assert::same(
452
            $this->generatePage->getPricesValidationMessage($position - 1),
453
            'You must define price for every channel.'
454
        );
455
    }
456
457
    /**
458
     * @Then /^I should be notified that variant code must be unique within this product for the (\d)(?:st|nd|rd|th) variant$/
459
     */
460
    public function iShouldBeNotifiedThatVariantCodeMustBeUniqueWithinThisProductForYheVariant($position)
461
    {
462
        Assert::same(
463
            $this->generatePage->getValidationMessage('code', $position - 1),
464
            'This code must be unique within this product.'
465
        );
466
    }
467
468
    /**
469
     * @Then I should be notified that prices in all channels must be defined
470
     */
471
    public function iShouldBeNotifiedThatPricesInAllChannelsMustBeDefined()
472
    {
473
        Assert::same(
474
            $this->createPage->getPricesValidationMessage(),
475
            'You must define price for every channel.'
476
        );
477
    }
478
479
    /**
480
     * @When I save my changes
481
     * @When I try to save my changes
482
     */
483
    public function iSaveMyChanges()
484
    {
485
        $this->updatePage->saveChanges();
486
    }
487
488
    /**
489
     * @When I remove its name
490
     */
491
    public function iRemoveItsNameFromTranslation()
492
    {
493
        $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...
494
    }
495
496
    /**
497
     * @Then /^the variant "([^"]+)" should have (\d+) items on hand$/
498
     */
499
    public function thisVariantShouldHaveItemsOnHand($productVariantName, $quantity)
500
    {
501
        Assert::true(
502
            $this->indexPage->isSingleResourceWithSpecificElementOnPage(['name' => $productVariantName], sprintf('td > div.ui.label:contains("%s")', $quantity)),
503
            sprintf('The product variant %s should have %s items on hand, but it does not.', $productVariantName, $quantity)
504
        );
505
    }
506
507
    /**
508
     * @Then /^the "([^"]+)" variant of ("[^"]+" product) should have (\d+) items on hand$/
509
     */
510
    public function theVariantOfProductShouldHaveItemsOnHand($productVariantName, ProductInterface $product, $quantity)
511
    {
512
        $this->indexPage->open(['productId' => $product->getId()]);
513
514
        Assert::true(
515
            $this->indexPage->isSingleResourceWithSpecificElementOnPage(['name' => $productVariantName], sprintf('td > div.ui.label:contains("%s")', $quantity)),
516
            sprintf('The product variant %s should have %s items on hand, but it does not.', $productVariantName, $quantity)
517
        );
518
    }
519
520
    /**
521
     * @Then /^inventory of (this variant) should not be tracked$/
522
     */
523
    public function thisProductVariantShouldNotBeTracked(ProductVariantInterface $productVariant)
524
    {
525
        $this->iWantToModifyAProduct($productVariant);
526
527
        Assert::false(
528
            $this->updatePage->isTracked(),
529
            'This variant should not be tracked, but it is.'
530
        );
531
    }
532
533
    /**
534
     * @Then /^inventory of (this variant) should be tracked$/
535
     */
536
    public function thisProductVariantShouldBeTracked(ProductVariantInterface $productVariant)
537
    {
538
        $this->iWantToModifyAProduct($productVariant);
539
540
        Assert::true(
541
            $this->updatePage->isTracked(),
542
            'This variant should be tracked, but it is not.'
543
        );
544
    }
545
546
    /**
547
     * @Then /^I should see that the ("([^"]+)" variant) is not tracked$/
548
     */
549
    public function iShouldSeeThatIsNotTracked(ProductVariantInterface $productVariant)
550
    {
551
        Assert::true(
552
            $this->indexPage->isSingleResourceOnPage(['name' => $productVariant->getName(), 'inventory' => 'Not tracked']),
553
            sprintf('This "%s" variant should have label not tracked, but it does not have', $productVariant->getName())
554
        );
555
    }
556
557
    /**
558
     * @Then /^I should see that the ("[^"]+" variant) has zero on hand quantity$/
559
     */
560
    public function iShouldSeeThatTheVariantHasZeroOnHandQuantity(ProductVariantInterface $productVariant)
561
    {
562
        Assert::true(
563
            $this->indexPage->isSingleResourceOnPage(['name' => $productVariant->getName(), 'inventory' => '0 Available on hand']),
564
            sprintf('This "%s" variant should have 0 on hand quantity, but it does not.', $productVariant->getName())
565
        );
566
    }
567
568
    /**
569
     * @Then /^(\d+) units of (this product) should be on hold$/
570
     */
571
    public function unitsOfThisProductShouldBeOnHold($quantity, ProductInterface $product)
572
    {
573
        /** @var ProductVariantInterface $variant */
574
        $variant = $this->defaultProductVariantResolver->getVariant($product);
575
576
        $this->assertOnHoldQuantityOfVariant($quantity, $variant);
577
    }
578
579
    /**
580
     * @Then /^(\d+) units of (this product) should be on hand$/
581
     */
582
    public function unitsOfThisProductShouldBeOnHand($quantity, ProductInterface $product)
583
    {
584
        /** @var ProductVariantInterface $variant */
585
        $variant = $this->defaultProductVariantResolver->getVariant($product);
586
        $actualQuantity = $this->indexPage->getOnHandQuantityFor($variant);
587
588
        Assert::same(
589
            (int) $quantity,
590
            $actualQuantity,
591
            sprintf(
592
                'Unexpected on hand quantity for "%s" variant. It should be "%s" but is "%s"',
593
                $variant->getName(),
594
                $quantity,
595
                $actualQuantity
596
            )
597
        );
598
    }
599
600
    /**
601
     * @Then /^there should be no units of (this product) on hold$/
602
     */
603
    public function thereShouldBeNoUnitsOfThisProductOnHold(ProductInterface $product)
604
    {
605
        /** @var ProductVariantInterface $variant */
606
        $variant = $this->defaultProductVariantResolver->getVariant($product);
607
608
        $this->assertOnHoldQuantityOfVariant(0, $variant);
609
    }
610
611
    /**
612
     * @Then the :variant variant should have :amount items on hold
613
     */
614
    public function thisVariantShouldHaveItemsOnHold(ProductVariantInterface $variant, $amount)
615
    {
616
        $this->assertOnHoldQuantityOfVariant((int) $amount, $variant);
617
    }
618
619
    /**
620
     * @Then the :variant variant of :product product should have :amount items on hold
621
     */
622
    public function theVariantOfProductShouldHaveItemsOnHold(ProductVariantInterface $variant, ProductInterface $product, $amount)
623
    {
624
        $this->indexPage->open(['productId' => $product->getId()]);
625
626
        $this->assertOnHoldQuantityOfVariant((int) $amount, $variant);
627
    }
628
629
    /**
630
     * @Then the first variant in the list should have :field :value
631
     */
632
    public function theFirstVariantInTheListShouldHave($field, $value)
633
    {
634
        $actualValue = $this->indexPage->getColumnFields($field)[0];
635
636
        Assert::same(
637
            $actualValue,
638
            $value,
639
            sprintf('Expected first variant\'s %s to be "%s", but it is "%s".', $field, $value, $actualValue)
640
        );
641
    }
642
643
    /**
644
     * @Then the last variant in the list should have :field :value
645
     */
646
    public function theLastVariantInTheListShouldHave($field, $value)
647
    {
648
        $fields = $this->indexPage->getColumnFields($field);
649
        $actualValue = end($fields);
650
651
        Assert::same(
652
            $actualValue,
653
            $value,
654
            sprintf('Expected last variant\'s %s to be "%s", but it is "%s".', $field, $value, $actualValue)
655
        );
656
    }
657
658
    /**
659
     * @When /^I want to generate new variants for (this product)$/
660
     */
661
    public function iWantToGenerateNewVariantsForThisProduct(ProductInterface $product)
662
    {
663
        $this->generatePage->open(['productId' => $product->getId()]);
664
    }
665
666
    /**
667
     * @When I generate it
668
     * @When I try to generate it
669
     */
670
    public function iClickGenerate()
671
    {
672
        $this->generatePage->generate();
673
    }
674
675
    /**
676
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant is identified by "([^"]+)" code and costs "(?:€|£|\$)([^"]+)" in ("[^"]+") channel$/
677
     */
678
    public function iSpecifyThereAreVariantsIdentifiedByCodeWithCost($nthVariant, $code, $price, $channelName)
679
    {
680
        $this->generatePage->specifyCode($nthVariant - 1, $code);
681
        $this->generatePage->specifyPrice($nthVariant - 1, $price, $channelName);
682
    }
683
684
    /**
685
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant is identified by "([^"]+)" code$/
686
     */
687
    public function iSpecifyThereAreVariantsIdentifiedByCode($nthVariant, $code)
688
    {
689
        $this->generatePage->specifyCode($nthVariant - 1, $code);
690
    }
691
692
    /**
693
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant costs "(?:€|£|\$)([^"]+)" in ("[^"]+") channel$/
694
     */
695
    public function iSpecifyThereAreVariantsWithCost($nthVariant, $price, $channelName)
696
    {
697
        $this->generatePage->specifyPrice($nthVariant - 1, $price, $channelName);
698
    }
699
700
    /**
701
     * @When /^I remove (\d)(?:st|nd|rd|th) variant from the list$/
702
     */
703
    public function iRemoveVariantFromTheList($nthVariant)
704
    {
705
        $this->generatePage->removeVariant($nthVariant - 1);
706
    }
707
708
    /**
709
     * @Then I should be notified that it has been successfully generated
710
     */
711
    public function iShouldBeNotifiedThatItHasBeenSuccessfullyGenerated()
712
    {
713
        $this->notificationChecker->checkNotification('Success Product variants have been successfully generated.', NotificationType::success());
714
    }
715
716
    /**
717
     * @When I set its shipping category as :shippingCategoryName
718
     */
719
    public function iSetItsShippingCategoryAs($shippingCategoryName)
720
    {
721
        $this->createPage->selectShippingCategory($shippingCategoryName);
722
    }
723
724
    /**
725
     * @When I do not specify any information about variants
726
     */
727
    public function iDoNotSpecifyAnyInformationAboutVariants()
728
    {
729
        // Intentionally left blank to fulfill context expectation
730
    }
731
    
732
    /**
733
     * @When I change its quantity of inventory to :amount
734
     */
735
    public function iChangeItsQuantityOfInventoryTo($amount)
736
    {
737
        $this->updatePage->specifyCurrentStock($amount);
738
    }
739
740
    /**
741
     * @Then /^(this variant) should have a (\d+) item currently in stock$/
742
     */
743
    public function thisVariantShouldHaveAItemCurrentlyInStock(ProductVariantInterface $productVariant, $amountInStock)
744
    {
745
        $this->indexPage->open(['productId' => $productVariant->getProduct()->getId()]);
746
747
        Assert::same(
748
            $this->indexPage->getOnHandQuantityFor($productVariant),
749
            (int) $amountInStock
750
        );
751
    }
752
753
    /**
754
     * @Then I should be notified that on hand quantity must be greater than the number of on hold units
755
     */
756
    public function iShouldBeNotifiedThatOnHandQuantityMustBeGreaterThanTheNumberOfOnHoldUnits()
757
    {
758
        Assert::same(
759
            $this->updatePage->getValidationMessage('on_hand'),
760
            'On hand must be greater than the number of on hold units'
761
        );
762
    }
763
764
    /**
765
     * @param string $element
766
     * @param $message
767
     */
768
    private function assertValidationMessage($element, $message)
769
    {
770
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
771
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
772
773
        Assert::same($currentPage->getValidationMessage($element), $message);
774
    }
775
776
    /**
777
     * @param int $expectedAmount
778
     * @param ProductVariantInterface $variant
779
     *
780
     * @throws \InvalidArgumentException
781
     */
782
    private function assertOnHoldQuantityOfVariant($expectedAmount, $variant)
783
    {
784
        $actualAmount = $this->indexPage->getOnHoldQuantityFor($variant);
785
786
        Assert::same(
787
            (int) $expectedAmount,
788
            $actualAmount,
789
            sprintf(
790
                'Unexpected on hold quantity for "%s" variant. It should be "%s" but is "%s"',
791
                $variant->getName(),
792
                $expectedAmount,
793
                $actualAmount
794
            )
795
        );
796
    }
797
798
    /**
799
     * @param ProductInterface $product
800
     * @param int $amount
801
     */
802
    private function assertNumberOfVariantsOnProductPage(ProductInterface $product, $amount)
803
    {
804
        $this->iWantToViewAllVariantsOfThisProduct($product);
805
806
        Assert::same((int) $this->indexPage->countItems(), $amount, 'Product has %d variants, but should have %d');
807
    }
808
}
809