Completed
Push — symfony3-wololo-packages ( fecf70...6bf04d )
by Kamil
28:46 queued 11:35
created

ManagingProductVariantsContext::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 19
rs 9.4285
cc 1
eloc 17
nc 1
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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\Component\Core\Model\ChannelInterface;
23
use Sylius\Component\Core\Model\ProductInterface;
24
use Sylius\Component\Core\Model\ProductVariantInterface;
25
use Sylius\Behat\Service\SharedStorageInterface;
26
use Sylius\Component\Currency\Model\CurrencyInterface;
27
use Sylius\Component\Product\Resolver\DefaultProductVariantResolver;
28
use Webmozart\Assert\Assert;
29
30
/**
31
 * @author Łukasz Chruściel <[email protected]>
32
 */
33
final class ManagingProductVariantsContext implements Context
34
{
35
    /**
36
     * @var SharedStorageInterface
37
     */
38
    private $sharedStorage;
39
40
    /**
41
     * @var DefaultProductVariantResolver
42
     */
43
    private $defaultProductVariantResolver;
44
45
    /**
46
     * @var CreatePageInterface
47
     */
48
    private $createPage;
49
50
    /**
51
     * @var IndexPageInterface
52
     */
53
    private $indexPage;
54
55
    /**
56
     * @var UpdatePageInterface
57
     */
58
    private $updatePage;
59
60
    /**
61
     * @var GeneratePageInterface
62
     */
63
    private $generatePage;
64
65
    /**
66
     * @var CurrentPageResolverInterface
67
     */
68
    private $currentPageResolver;
69
70
    /**
71
     * @var NotificationCheckerInterface
72
     */
73
    private $notificationChecker;
74
75
    /**
76
     * @param SharedStorageInterface $sharedStorage
77
     * @param DefaultProductVariantResolver $defaultProductVariantResolver
78
     * @param CreatePageInterface $createPage
79
     * @param IndexPageInterface $indexPage
80
     * @param UpdatePageInterface $updatePage
81
     * @param GeneratePageInterface $generatePage
82
     * @param CurrentPageResolverInterface $currentPageResolver
83
     * @param NotificationCheckerInterface $notificationChecker
84
     */
85
    public function __construct(
86
        SharedStorageInterface $sharedStorage,
87
        DefaultProductVariantResolver $defaultProductVariantResolver,
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $defaultProductVariantResolver exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
88
        CreatePageInterface $createPage,
89
        IndexPageInterface $indexPage,
90
        UpdatePageInterface $updatePage,
91
        GeneratePageInterface $generatePage,
92
        CurrentPageResolverInterface $currentPageResolver,
93
        NotificationCheckerInterface $notificationChecker
94
    ) {
95
        $this->sharedStorage = $sharedStorage;
96
        $this->defaultProductVariantResolver = $defaultProductVariantResolver;
97
        $this->createPage = $createPage;
98
        $this->indexPage = $indexPage;
99
        $this->updatePage = $updatePage;
100
        $this->generatePage = $generatePage;
101
        $this->currentPageResolver = $currentPageResolver;
102
        $this->notificationChecker = $notificationChecker;
103
    }
104
105
    /**
106
     * @Given /^I want to create a new variant of (this product)$/
107
     */
108
    public function iWantToCreateANewProduct(ProductInterface $product)
109
    {
110
        $this->createPage->open(['productId' => $product->getId()]);
111
    }
112
113
    /**
114
     * @When I specify its code as :code
115
     * @When I do not specify its code
116
     */
117
    public function iSpecifyItsCodeAs($code = null)
118
    {
119
        $this->createPage->specifyCode($code);
120
    }
121
122
    /**
123
     * @When I name it :name
124
     */
125
    public function iNameItIn($name)
126
    {
127
        $this->createPage->nameIt($name);
128
    }
129
130
    /**
131
     * @When I rename it to :name
132
     */
133
    public function iRenameItTo($name)
134
    {
135
        $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...
136
    }
137
138
    /**
139
     * @When I add it
140
     * @When I try to add it
141
     */
142
    public function iAddIt()
143
    {
144
        $this->createPage->create();
145
    }
146
147
    /**
148
     * @When I disable its inventory tracking
149
     */
150
    public function iDisableItsTracking()
151
    {
152
        $this->updatePage->disableTracking();
153
    }
154
155
    /**
156
     * @When I enable its inventory tracking
157
     */
158
    public function iEnableItsTracking()
159
    {
160
        $this->updatePage->enableTracking();
161
    }
162
163
    /**
164
     * @When /^I set its(?:| default) price to "(?:€|£|\$)([^"]+)" for "([^"]+)" channel$/
165
     * @When I do not set its price
166
     */
167
    public function iSetItsPriceTo($price = null, $channel = null)
168
    {
169
        $this->createPage->specifyPrice($price, (null === $channel) ? $this->sharedStorage->get('channel') :$channel);
170
    }
171
172
    /**
173
     * @When I set its height, width, depth and weight to :number
174
     */
175
    public function iSetItsDimensionsTo($value)
176
    {
177
        $this->createPage->specifyHeightWidthDepthAndWeight($value, $value, $value, $value);
178
    }
179
180
    /**
181
     * @When I do not specify its current stock
182
     */
183
    public function iDoNetSetItsCurrentStockTo()
184
    {
185
        $this->createPage->specifyCurrentStock('');
186
    }
187
188
    /**
189
     * @When I choose :calculatorName calculator
190
     */
191
    public function iChooseCalculator($calculatorName)
192
    {
193
        $this->createPage->choosePricingCalculator($calculatorName);
194
    }
195
196
    /**
197
     * @When /^I set its price to "(?:€|£|\$)([^"]+)" for ("[^"]+" currency) and ("[^"]+" channel)$/
198
     */
199
    public function iSetItsPriceToForCurrencyAndChannel($price, CurrencyInterface $currency, ChannelInterface $channel)
200
    {
201
        $this->createPage->specifyPriceForChannelAndCurrency($price, $channel, $currency);
202
    }
203
204
    /**
205
     * @When I set its :optionName option to :optionValue
206
     */
207
    public function iSetItsOptionAs($optionName, $optionValue)
208
    {
209
        $this->createPage->selectOption($optionName, $optionValue);
210
    }
211
212
    /**
213
     * @Then the :productVariantCode variant of the :product product should appear in the store
214
     */
215
    public function theProductVariantShouldAppearInTheShop($productVariantCode, ProductInterface $product)
216
    {
217
        $this->iWantToViewAllVariantsOfThisProduct($product);
218
219
        Assert::true(
220
            $this->indexPage->isSingleResourceOnPage(['code' => $productVariantCode]),
221
            sprintf('The product variant with code %s has not been found.', $productVariantCode)
222
        );
223
    }
224
225
    /**
226
     * @Then the :productVariantCode variant of the :product product should not appear in the store
227
     */
228
    public function theProductVariantShouldNotAppearInTheShop($productVariantCode, ProductInterface $product)
229
    {
230
        $this->iWantToViewAllVariantsOfThisProduct($product);
231
232
        Assert::false(
233
            $this->indexPage->isSingleResourceOnPage(['code' => $productVariantCode]),
234
            sprintf('The product variant with code %s has not been found.', $productVariantCode)
235
        );
236
    }
237
238
    /**
239
     * @Then the :product product should have no variants
240
     */
241
    public function theProductShouldHaveNoVariants(ProductInterface $product)
242
    {
243
        $this->assertNumberOfVariantsOnProductPage($product, 0);
244
    }
245
246
    /**
247
     * @Then the :product product should have only one variant
248
     */
249
    public function theProductShouldHaveOnlyOneVariant(ProductInterface $product)
250
    {
251
        $this->assertNumberOfVariantsOnProductPage($product, 1);
252
    }
253
254
    /**
255
     * @Then /^the (variant with code "[^"]+") should be priced at (?:€|£|\$)([^"]+) for channel "([^"]+)"$/
256
     */
257
    public function theVariantWithCodeShouldBePricedAtForChannel(ProductVariantInterface $productVariant, $price, $channelName)
258
    {
259
        $this->updatePage->open(['id' => $productVariant->getId(), 'productId' => $productVariant->getProduct()->getId()]);
260
261
        Assert::same(
262
            $this->updatePage->getPriceForChannel($channelName),
263
            $price
264
        );
265
    }
266
267
    /**
268
     * @When /^I (?:|want to )view all variants of (this product)$/
269
     * @When /^I view(?:| all) variants of the (product "[^"]+")$/
270
     */
271
    public function iWantToViewAllVariantsOfThisProduct(ProductInterface $product)
272
    {
273
        $this->indexPage->open(['productId' => $product->getId()]);
274
    }
275
276
    /**
277
     * @Then I should see :numberOfProductVariants variants in the list
278
     * @Then I should see :numberOfProductVariants variant in the list
279
     */
280
    public function iShouldSeeProductVariantsInTheList($numberOfProductVariants)
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $numberOfProductVariants exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
281
    {
282
        $foundRows = $this->indexPage->countItems();
283
284
        Assert::same(
285
            (int) $numberOfProductVariants,
286
            $foundRows,
287
            '%s rows with product variants should appear on page, %s rows has been found'
288
        );
289
    }
290
291
    /**
292
     * @When /^I delete the ("[^"]+" variant of product "[^"]+")$/
293
     * @When /^I try to delete the ("[^"]+" variant of product "[^"]+")$/
294
     */
295
    public function iDeleteTheVariantOfProduct(ProductVariantInterface $productVariant)
296
    {
297
        $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...
298
299
        $this->indexPage->deleteResourceOnPage(['code' => $productVariant->getCode()]);
300
    }
301
302
    /**
303
     * @Then /^(this variant) should not exist in the product catalog$/
304
     */
305
    public function productVariantShouldNotExist(ProductVariantInterface $productVariant)
306
    {
307
        $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...
308
309
        Assert::false(
310
            $this->indexPage->isSingleResourceOnPage(['name' => $productVariant->getName()]),
311
            sprintf('Product variant with code %s exists but should not.', $productVariant->getName())
312
        );
313
    }
314
315
    /**
316
     * @Then I should be notified that this variant is in use and cannot be deleted
317
     */
318
    public function iShouldBeNotifiedOfFailure()
319
    {
320
        $this->notificationChecker->checkNotification(
321
            "Cannot delete, the product variant is in use.",
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal Cannot delete, the product variant 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...
322
            NotificationType::failure()
323
        );
324
    }
325
326
    /**
327
     * @Then /^(this variant) should still exist in the product catalog$/
328
     */
329
    public function productShouldExistInTheProductCatalog(ProductVariantInterface $productVariant)
330
    {
331
        $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...
332
    }
333
334
    /**
335
     * @When /^I want to modify the ("[^"]+" product variant)$/
336
     * @When /^I want to modify (this product variant)$/
337
     */
338
    public function iWantToModifyAProduct(ProductVariantInterface $productVariant)
339
    {
340
        $this->updatePage->open(['id' => $productVariant->getId(), 'productId' => $productVariant->getProduct()->getId()]);
341
    }
342
343
    /**
344
     * @Then the code field should be disabled
345
     */
346
    public function theCodeFieldShouldBeDisabled()
347
    {
348
        Assert::true(
349
            $this->updatePage->isCodeDisabled(),
350
            'Code should be immutable, but it does not.'
351
        );
352
    }
353
354
    /**
355
     * @Then I should be notified that :element is required
356
     */
357
    public function iShouldBeNotifiedThatIsRequired($element)
358
    {
359
        $this->assertValidationMessage($element, sprintf('Please enter the %s.', $element));
360
    }
361
362
    /**
363
     * @Then I should be notified that code has to be unique
364
     */
365
    public function iShouldBeNotifiedThatCodeHasToBeUnique()
366
    {
367
        $this->assertValidationMessage('code', 'Product variant code must be unique.');
368
    }
369
370
    /**
371
     * @Then I should be notified that current stock is required
372
     */
373
    public function iShouldBeNotifiedThatOnHandIsRequired()
374
    {
375
        $this->assertValidationMessage('on_hand', 'Please enter on hand.');
376
    }
377
378
    /**
379
     * @Then I should be notified that height, width, depth and weight cannot be lower than 0
380
     */
381
    public function iShouldBeNotifiedThatIsHeightWidthDepthWeightCannotBeLowerThan()
382
    {
383
        $this->assertValidationMessage('height', 'Height cannot be negative.');
384
        $this->assertValidationMessage('width', 'Width cannot be negative.');
385
        $this->assertValidationMessage('depth', 'Depth cannot be negative.');
386
        $this->assertValidationMessage('weight', 'Weight cannot be negative.');
387
    }
388
389
    /**
390
     * @Then I should be notified that price cannot be lower than 0
391
     */
392
    public function iShouldBeNotifiedThatPriceCannotBeLowerThen()
393
    {
394
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
395
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
396
397
        Assert::same($currentPage->getPricesValidationMessage(), 'Price must not be negative.');
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...
398
    }
399
400
    /**
401
     * @Then I should be notified that this variant already exists
402
     */
403
    public function iShouldBeNotifiedThatThisVariantAlreadyExists()
404
    {
405
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
406
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
407
408
        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...
409
    }
410
411
    /**
412
     * @Then /^I should be notified that code is required for the (\d)(?:st|nd|rd|th) variant$/
413
     */
414
    public function iShouldBeNotifiedThatCodeIsRequiredForVariant($position)
415
    {
416
        Assert::same(
417
            $this->generatePage->getValidationMessage('code', $position),
418
            'Please enter the code.'
419
        );
420
    }
421
422
    /**
423
     * @Then /^I should be notified that prices in all channels must be defined for the (\d)(?:st|nd|rd|th) variant$/
424
     */
425
    public function iShouldBeNotifiedThatPricesInAllChannelsMustBeDefinedForTheVariant($position)
426
    {
427
        Assert::same(
428
            $this->generatePage->getPricesValidationMessage($position-1),
429
            'You must define price for every channel.'
430
        );
431
    }
432
433
    /**
434
     * @Then I should be notified that prices in all channels must be defined
435
     */
436
    public function iShouldBeNotifiedThatPricesInAllChannelsMustBeDefined()
437
    {
438
        Assert::same(
439
            $this->createPage->getPricesValidationMessage(),
440
            'You must define price for every channel.'
441
        );
442
    }
443
444
    /**
445
     * @When I save my changes
446
     * @When I try to save my changes
447
     */
448
    public function iSaveMyChanges()
449
    {
450
        $this->updatePage->saveChanges();
451
    }
452
453
    /**
454
     * @When /^I change its price to "(?:€|£|\$)([^"]+)"$/
455
     */
456
    public function iChangeItsPriceTo($price)
457
    {
458
        $this->updatePage->specifyPrice($price);
459
    }
460
461
    /**
462
     * @When I remove its name
463
     */
464
    public function iRemoveItsNameFromTranslation()
465
    {
466
        $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...
467
    }
468
469
    /**
470
     * @Then /^the variant "([^"]+)" should have (\d+) items on hand$/
471
     */
472
    public function thisVariantShouldHaveItemsOnHand($productVariantName, $quantity)
473
    {
474
        Assert::true(
475
            $this->indexPage->isSingleResourceWithSpecificElementOnPage(['name' => $productVariantName], sprintf('td > div.ui.label:contains("%s")', $quantity)),
476
            sprintf('The product variant %s should have %s items on hand, but it does not.',$productVariantName, $quantity)
477
        );
478
    }
479
480
    /**
481
     * @Then /^the "([^"]+)" variant of ("[^"]+" product) should have (\d+) items on hand$/
482
     */
483
    public function theVariantOfProductShouldHaveItemsOnHand($productVariantName, ProductInterface $product, $quantity)
484
    {
485
        $this->indexPage->open(['productId' => $product->getId()]);
486
487
        Assert::true(
488
            $this->indexPage->isSingleResourceWithSpecificElementOnPage(['name' => $productVariantName], sprintf('td > div.ui.label:contains("%s")', $quantity)),
489
            sprintf('The product variant %s should have %s items on hand, but it does not.',$productVariantName, $quantity)
490
        );
491
    }
492
493
    /**
494
     * @Then /^inventory of (this variant) should not be tracked$/
495
     */
496
    public function thisProductVariantShouldNotBeTracked(ProductVariantInterface $productVariant)
497
    {
498
        $this->iWantToModifyAProduct($productVariant);
499
500
        Assert::false(
501
            $this->updatePage->isTracked(),
502
            'This variant should not be tracked, but it is.'
503
        );
504
    }
505
506
    /**
507
     * @Then /^inventory of (this variant) should be tracked$/
508
     */
509
    public function thisProductVariantShouldBeTracked(ProductVariantInterface $productVariant)
510
    {
511
        $this->iWantToModifyAProduct($productVariant);
512
513
        Assert::true(
514
            $this->updatePage->isTracked(),
515
            'This variant should be tracked, but it is not.'
516
        );
517
    }
518
519
    /**
520
     * @Then /^I should see that the ("([^"]+)" variant) is not tracked$/
521
     */
522
    public function iShouldSeeThatIsNotTracked(ProductVariantInterface $productVariant)
523
    {
524
        Assert::true(
525
            $this->indexPage->isSingleResourceOnPage(['name' => $productVariant->getName(), 'inventory' => 'Not tracked']),
526
            sprintf('This "%s" variant should have label not tracked, but it does not have', $productVariant->getName())
527
        );
528
    }
529
530
    /**
531
     * @Then /^I should see that the ("[^"]+" variant) has zero on hand quantity$/
532
     */
533
    public function iShouldSeeThatTheVariantHasZeroOnHandQuantity(ProductVariantInterface $productVariant)
534
    {
535
        Assert::true(
536
            $this->indexPage->isSingleResourceOnPage(['name' => $productVariant->getName(), 'inventory' => '0 Available on hand']),
537
            sprintf('This "%s" variant should have 0 on hand quantity, but it does not.', $productVariant->getName())
538
        );
539
    }
540
541
    /**
542
     * @Then /^(\d+) units of (this product) should be on hold$/
543
     */
544
    public function unitsOfThisProductShouldBeOnHold($quantity, ProductInterface $product)
545
    {
546
        /** @var ProductVariantInterface $variant */
547
        $variant = $this->defaultProductVariantResolver->getVariant($product);
548
549
        $this->assertOnHoldQuantityOfVariant($quantity, $variant);
550
    }
551
552
    /**
553
     * @Then /^(\d+) units of (this product) should be on hand$/
554
     */
555
    public function unitsOfThisProductShouldBeOnHand($quantity, ProductInterface $product)
556
    {
557
        /** @var ProductVariantInterface $variant */
558
        $variant = $this->defaultProductVariantResolver->getVariant($product);
559
        $actualQuantity = $this->indexPage->getOnHandQuantityFor($variant);
560
561
        Assert::same(
562
            (int) $quantity,
563
            $actualQuantity,
564
            sprintf(
565
                'Unexpected on hand quantity for "%s" variant. It should be "%s" but is "%s"',
566
                $variant->getName(),
567
                $quantity,
568
                $actualQuantity
569
            )
570
        );
571
    }
572
573
    /**
574
     * @Then /^there should be no units of (this product) on hold$/
575
     */
576
    public function thereShouldBeNoUnitsOfThisProductOnHold(ProductInterface $product)
577
    {
578
        /** @var ProductVariantInterface $variant */
579
        $variant = $this->defaultProductVariantResolver->getVariant($product);
580
581
        $this->assertOnHoldQuantityOfVariant(0, $variant);
582
    }
583
584
    /**
585
     * @Then the :variant variant should have :amount items on hold
586
     * @Then /^(this variant) should have (\d+) items on hold$/
587
     */
588
    public function thisVariantShouldHaveItemsOnHold(ProductVariantInterface $variant, $amount)
589
    {
590
        $this->assertOnHoldQuantityOfVariant((int) $amount, $variant);
591
    }
592
593
    /**
594
     * @Then the :variant variant of :product product should have :amount items on hold
595
     */
596
    public function theVariantOfProductShouldHaveItemsOnHold(ProductVariantInterface $variant, ProductInterface $product, $amount)
597
    {
598
        $this->indexPage->open(['productId' => $product->getId()]);
599
600
        $this->assertOnHoldQuantityOfVariant((int) $amount, $variant);
601
    }
602
603
    /**
604
     * @Then /^(variant with code "[^"]+") for ("[^"]+" currency) and ("[^"]+" channel) should be priced at "(?:€|£|\$)([^"]+)"$/
605
     */
606
    public function theProductForCurrencyAndChannelShouldBePricedAt(
607
        ProductVariantInterface $productVariant,
608
        CurrencyInterface $currency,
609
        ChannelInterface $channel,
610
        $price
611
    ) {
612
        $this->updatePage->open(['id' => $productVariant->getId(), 'productId' => $productVariant->getProduct()->getId()]);
613
614
        Assert::same(
615
            $this->updatePage->getPricingConfigurationForChannelAndCurrencyCalculator($channel, $currency),
616
            $price
617
        );
618
    }
619
620
    /**
621
     * @Given /^I want to generate new variants for (this product)$/
622
     */
623
    public function iWantToGenerateNewVariantsForThisProduct(ProductInterface $product)
624
    {
625
        $this->generatePage->open(['productId' => $product->getId()]);
626
    }
627
628
    /**
629
     * @When I generate it
630
     * @When I try to generate it
631
     */
632
    public function iClickGenerate()
633
    {
634
        $this->generatePage->generate();
635
    }
636
637
    /**
638
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant is identified by ("[^"]+") code and costs "(?:€|£|\$)([^"]+)" in ("[^"]+") channel$/
639
     */
640
    public function iSpecifyThereAreVariantsIdentifiedByCodeWithCost($nthVariant, $code, $price, $channelName)
641
    {
642
        $this->generatePage->nameCode($nthVariant - 1, $code);
643
        $this->generatePage->specifyPrice($nthVariant - 1, $price, $channelName);
644
    }
645
646
    /**
647
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant is identified by ("[^"]+") code$/
648
     */
649
    public function iSpecifyThereAreVariantsIdentifiedByCode($nthVariant, $code)
650
    {
651
        $this->generatePage->nameCode($nthVariant, $code - 1);
652
    }
653
654
    /**
655
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant costs "(?:€|£|\$)([^"]+)" in ("[^"]+") channel$/
656
     */
657
    public function iSpecifyThereAreVariantsWithCost($nthVariant, $price, $channelName)
658
    {
659
        $this->generatePage->specifyPrice($nthVariant, $price - 1, $channelName);
660
    }
661
662
    /**
663
     * @When /^I remove (\d)(?:st|nd|rd|th) variant from the list$/
664
     */
665
    public function iRemoveVariantFromTheList($nthVariant)
666
    {
667
        $this->generatePage->removeVariant($nthVariant - 1);
668
    }
669
670
    /**
671
     * @Then I should be notified that it has been successfully generated
672
     */
673
    public function iShouldBeNotifiedThatItHasBeenSuccessfullyGenerated()
674
    {
675
        $this->notificationChecker->checkNotification('Success Product variants have been successfully generated.', NotificationType::success());
676
    }
677
678
    /**
679
     * @When I set its shipping category as :shippingCategoryName
680
     */
681
    public function iSetItsShippingCategoryAs($shippingCategoryName)
682
    {
683
        $this->createPage->selectShippingCategory($shippingCategoryName);
684
    }
685
686
    /**
687
     * @param string $element
688
     * @param $message
689
     */
690
    private function assertValidationMessage($element, $message)
691
    {
692
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
693
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
694
695
        Assert::same($currentPage->getValidationMessage($element), $message);
696
    }
697
698
    /**
699
     * @param int $expectedAmount
700
     * @param ProductVariantInterface $variant
701
     *
702
     * @throws \InvalidArgumentException
703
     */
704
    private function assertOnHoldQuantityOfVariant($expectedAmount, $variant)
705
    {
706
        $actualAmount = $this->indexPage->getOnHoldQuantityFor($variant);
707
708
        Assert::same(
709
            (int) $expectedAmount,
710
            $actualAmount,
711
            sprintf(
712
                'Unexpected on hold quantity for "%s" variant. It should be "%s" but is "%s"',
713
                $variant->getName(),
714
                $expectedAmount,
715
                $actualAmount
716
            )
717
        );
718
    }
719
720
    /**
721
     * @param ProductInterface $product
722
     * @param int $amount
723
     */
724
    private function assertNumberOfVariantsOnProductPage(ProductInterface $product, $amount)
725
    {
726
        $this->iWantToViewAllVariantsOfThisProduct($product);
727
728
        Assert::same((int) $this->indexPage->countItems(), $amount, 'Product has %d variants, but should have %d');
729
    }
730
}
731