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

ManagingProductVariantsContext   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 649
Duplicated Lines 0 %

Coupling/Cohesion

Components 4
Dependencies 12

Importance

Changes 0
Metric Value
wmc 58
lcom 4
cbo 12
dl 0
loc 649
rs 4.3644
c 0
b 0
f 0

58 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 19 1
A iWantToCreateANewProduct() 0 4 1
A iSpecifyItsCodeAs() 0 4 1
A iNameItIn() 0 4 1
A iRenameItTo() 0 4 1
A iAddIt() 0 4 1
A iDisableItsTracking() 0 4 1
A iEnableItsTracking() 0 4 1
A iSetItsPriceTo() 0 4 1
A iSetItsDimensionsTo() 0 4 1
A iDoNetSetItsCurrentStockTo() 0 4 1
A iChooseCalculator() 0 4 1
A iSetItsPriceToForCurrencyAndChannel() 0 4 1
A iSetItsOptionAs() 0 4 1
A theProductVariantShouldAppearInTheShop() 0 9 1
A theProductVariantShouldNotAppearInTheShop() 0 9 1
A theProductShouldHaveNoVariants() 0 4 1
A theProductShouldHaveOnlyOneVariant() 0 4 1
A iWantToViewAllVariantsOfThisProduct() 0 4 1
A iShouldSeeProductVariantsInTheList() 0 10 1
A iDeleteTheVariantOfProduct() 0 6 1
A productVariantShouldNotExist() 0 9 1
A iShouldBeNotifiedOfFailure() 0 7 1
A productShouldExistInTheProductCatalog() 0 4 1
A iWantToModifyAProduct() 0 4 1
A theCodeFieldShouldBeDisabled() 0 7 1
A iShouldBeNotifiedThatIsRequired() 0 4 1
A iShouldBeNotifiedThatCodeHasToBeUnique() 0 4 1
A iShouldBeNotifiedThatOnHandIsRequired() 0 4 1
A iShouldBeNotifiedThatIsHeightWidthDepthWeightCannotBeLowerThan() 0 7 1
A iShouldBeNotifiedThatCannotBeLowerThen() 0 4 1
A iShouldBeNotifiedThatThisVariantAlreadyExists() 0 7 1
A iShouldBeNotifiedThatIsRequiredForVariant() 0 4 1
A iSaveMyChanges() 0 4 1
A iChangeItsPriceTo() 0 4 1
A iRemoveItsNameFromTranslation() 0 4 1
A thisVariantShouldHaveItemsOnHand() 0 7 1
A theVariantOfProductShouldHaveItemsOnHand() 0 9 1
A thisProductVariantShouldNotBeTracked() 0 9 1
A thisProductVariantShouldBeTracked() 0 9 1
A iShouldSeeThatIsNotTracked() 0 7 1
A iShouldSeeThatTheVariantHasZeroOnHandQuantity() 0 7 1
A unitsOfThisProductShouldBeOnHold() 0 7 1
A unitsOfThisProductShouldBeOnHand() 0 17 1
A thereShouldBeNoUnitsOfThisProductOnHold() 0 7 1
A thisVariantShouldHaveItemsOnHold() 0 4 1
A theVariantOfProductShouldHaveItemsOnHold() 0 6 1
A theProductForCurrencyAndChannelShouldBePricedAt() 0 13 1
A iWantToGenerateNewVariantsForThisProduct() 0 4 1
A iClickGenerate() 0 4 1
A iSpecifyThereAreVariantsIdentifiedByCodeWithCost() 0 5 1
A iSpecifyThereAreVariantsIdentifiedByCode() 0 4 1
A iSpecifyThereAreVariantsWithCost() 0 4 1
A iRemoveVariantFromTheList() 0 4 1
A iShouldBeNotifiedThatItHasBeenSuccessfullyGenerated() 0 4 1
A assertValidationMessage() 0 7 1
A assertOnHoldQuantityOfVariant() 0 15 1
A assertNumberOfVariantsOnProductPage() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like ManagingProductVariantsContext often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ManagingProductVariantsContext, and based on these observations, apply Extract Interface, too.

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