Completed
Push — 1.3 ( 2923d8...a36271 )
by Kamil
32:20
created

iSpecifyThereAreVariantsIdentifiedByCode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
324
    }
325
326
    /**
327
     * @Then I should be notified that this variant already exists
328
     */
329
    public function iShouldBeNotifiedThatThisVariantAlreadyExists()
330
    {
331
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
332
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
333
334
        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...
335
    }
336
337
    /**
338
     * @Then /^I should be notified that code is required for the (\d)(?:st|nd|rd|th) variant$/
339
     */
340
    public function iShouldBeNotifiedThatCodeIsRequiredForVariant($position)
341
    {
342
        Assert::same(
343
            $this->generatePage->getValidationMessage('code', $position - 1),
344
            'Please enter the code.'
345
        );
346
    }
347
348
    /**
349
     * @Then /^I should be notified that prices in all channels must be defined for the (\d)(?:st|nd|rd|th) variant$/
350
     */
351
    public function iShouldBeNotifiedThatPricesInAllChannelsMustBeDefinedForTheVariant($position)
352
    {
353
        Assert::same(
354
            $this->generatePage->getPricesValidationMessage($position - 1),
355
            'You must define price for every channel.'
356
        );
357
    }
358
359
    /**
360
     * @Then /^I should be notified that variant code must be unique within this product for the (\d)(?:st|nd|rd|th) variant$/
361
     */
362
    public function iShouldBeNotifiedThatVariantCodeMustBeUniqueWithinThisProductForYheVariant($position)
363
    {
364
        Assert::same(
365
            $this->generatePage->getValidationMessage('code', $position - 1),
366
            'This code must be unique within this product.'
367
        );
368
    }
369
370
    /**
371
     * @Then I should be notified that prices in all channels must be defined
372
     */
373
    public function iShouldBeNotifiedThatPricesInAllChannelsMustBeDefined()
374
    {
375
        Assert::contains(
376
            $this->createPage->getPricesValidationMessage(),
377
            'You must define price for every channel.'
378
        );
379
    }
380
381
    /**
382
     * @When I save my changes
383
     * @When I try to save my changes
384
     */
385
    public function iSaveMyChanges()
386
    {
387
        $this->updatePage->saveChanges();
388
    }
389
390
    /**
391
     * @Then /^inventory of (this variant) should not be tracked$/
392
     */
393
    public function thisProductVariantShouldNotBeTracked(ProductVariantInterface $productVariant)
394
    {
395
        $this->iWantToModifyAProduct($productVariant);
396
397
        Assert::false($this->updatePage->isTracked());
398
    }
399
400
    /**
401
     * @Then /^inventory of (this variant) should be tracked$/
402
     */
403
    public function thisProductVariantShouldBeTracked(ProductVariantInterface $productVariant)
404
    {
405
        $this->iWantToModifyAProduct($productVariant);
406
407
        Assert::true($this->updatePage->isTracked());
408
    }
409
410
    /**
411
     * @When I generate it
412
     * @When I try to generate it
413
     */
414
    public function iClickGenerate()
415
    {
416
        $this->generatePage->generate();
417
    }
418
419
    /**
420
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant is identified by "([^"]+)" code and costs "(?:€|£|\$)([^"]+)" in ("[^"]+") channel$/
421
     */
422
    public function iSpecifyThereAreVariantsIdentifiedByCodeWithCost($nthVariant, $code, int $price, $channelName)
423
    {
424
        $this->generatePage->specifyCode($nthVariant - 1, $code);
425
        $this->generatePage->specifyPrice($nthVariant - 1, $price, $channelName);
426
    }
427
428
    /**
429
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant is identified by "([^"]+)" code$/
430
     */
431
    public function iSpecifyThereAreVariantsIdentifiedByCode($nthVariant, $code)
432
    {
433
        $this->generatePage->specifyCode($nthVariant - 1, $code);
434
    }
435
436
    /**
437
     * @When /^I specify that the (\d)(?:st|nd|rd|th) variant costs "(?:€|£|\$)([^"]+)" in ("[^"]+") channel$/
438
     */
439
    public function iSpecifyThereAreVariantsWithCost($nthVariant, int $price, $channelName)
440
    {
441
        $this->generatePage->specifyPrice($nthVariant - 1, $price, $channelName);
442
    }
443
444
    /**
445
     * @When /^I remove (\d)(?:st|nd|rd|th) variant from the list$/
446
     */
447
    public function iRemoveVariantFromTheList($nthVariant)
448
    {
449
        $this->generatePage->removeVariant($nthVariant - 1);
450
    }
451
452
    /**
453
     * @Then I should be notified that it has been successfully generated
454
     */
455
    public function iShouldBeNotifiedThatItHasBeenSuccessfullyGenerated()
456
    {
457
        $this->notificationChecker->checkNotification('Success Product variants have been successfully generated.', NotificationType::success());
458
    }
459
460
    /**
461
     * @Then I should not be able to generate any variants
462
     */
463
    public function iShouldNotBeAbleToGenerateAnyVariants(): void
464
    {
465
        Assert::false($this->generatePage->isGenerationPossible());
466
    }
467
468
    /**
469
     * @When I set its shipping category as :shippingCategoryName
470
     */
471
    public function iSetItsShippingCategoryAs($shippingCategoryName)
472
    {
473
        $this->createPage->selectShippingCategory($shippingCategoryName);
474
    }
475
476
    /**
477
     * @When I do not specify any information about variants
478
     */
479
    public function iDoNotSpecifyAnyInformationAboutVariants()
480
    {
481
        // Intentionally left blank to fulfill context expectation
482
    }
483
484
    /**
485
     * @When I change its quantity of inventory to :amount
486
     */
487
    public function iChangeItsQuantityOfInventoryTo(int $amount)
488
    {
489
        $this->updatePage->specifyCurrentStock($amount);
490
    }
491
492
    /**
493
     * @Then /^the (variant with code "[^"]+") should not have shipping required$/
494
     */
495
    public function theVariantWithCodeShouldNotHaveShippingRequired(ProductVariantInterface $productVariant)
496
    {
497
        $this->updatePage->open(['productId' => $productVariant->getProduct()->getId(), 'id' => $productVariant->getId()]);
498
499
        Assert::false($this->updatePage->isShippingRequired());
500
    }
501
502
    /**
503
     * @Then I should be notified that on hand quantity must be greater than the number of on hold units
504
     */
505
    public function iShouldBeNotifiedThatOnHandQuantityMustBeGreaterThanTheNumberOfOnHoldUnits()
506
    {
507
        Assert::same(
508
            $this->updatePage->getValidationMessage('on_hand'),
509
            'On hand must be greater than the number of on hold units'
510
        );
511
    }
512
513
    /**
514
     * @Then I should be notified that variants cannot be generated from options without any values
515
     */
516
    public function iShouldBeNotifiedThatVariantsCannotBeGeneratedFromOptionsWithoutAnyValues(): void
517
    {
518
        $this->notificationChecker->checkNotification('Cannot generate variants for a product without options values', NotificationType::failure());
519
    }
520
521
    /**
522
     * @When /^I want to generate new variants for (this product)$/
523
     * @When /^I try to generate new variants for (this product)$/
524
     */
525
    public function iTryToGenerateNewVariantsForThisProduct(ProductInterface $product): void
526
    {
527
        $this->generatePage->open(['productId' => $product->getId()]);
528
    }
529
530
    /**
531
     * @param string $element
532
     * @param string $message
533
     */
534
    private function assertValidationMessage($element, $message)
535
    {
536
        /** @var CreatePageInterface|UpdatePageInterface $currentPage */
537
        $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
538
539
        Assert::same($currentPage->getValidationMessage($element), $message);
540
    }
541
}
542