Completed
Push — php71-support ( 2570a5...950570 )
by Kamil
30:36
created

iShouldBeNotifiedThatThereAreNoReviews()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sylius\Behat\Context\Ui\Shop;
13
14
use Behat\Behat\Context\Context;
15
use Behat\Mink\Element\NodeElement;
16
use Sylius\Behat\Page\Shop\Product\IndexPageInterface;
17
use Sylius\Behat\Page\Shop\Product\ShowPageInterface;
18
use Sylius\Behat\Page\Shop\ProductReview\IndexPageInterface as ProductReviewIndexPageInterface;
19
use Sylius\Component\Core\Model\ProductInterface;
20
use Sylius\Component\Core\Model\TaxonInterface;
21
use Webmozart\Assert\Assert;
22
23
/**
24
 * @author Kamil Kokot <[email protected]>
25
 * @author Magdalena Banasiak <[email protected]>
26
 * @author Anna Walasek <[email protected]>
27
 */
28
final class ProductContext implements Context
29
{
30
    /**
31
     * @var ShowPageInterface
32
     */
33
    private $showPage;
34
35
    /**
36
     * @var IndexPageInterface
37
     */
38
    private $indexPage;
39
40
    /**
41
     * @var ProductReviewIndexPageInterface
42
     */
43
    private $productReviewsIndexPage;
44
45
    /**
46
     * @param ShowPageInterface $showPage
47
     * @param IndexPageInterface $indexPage
48
     * @param ProductReviewIndexPageInterface $productReviewsIndexPage
49
     */
50
    public function __construct(
51
        ShowPageInterface $showPage,
52
        IndexPageInterface $indexPage,
53
        ProductReviewIndexPageInterface $productReviewsIndexPage
54
    ) {
55
        $this->showPage = $showPage;
56
        $this->indexPage = $indexPage;
57
        $this->productReviewsIndexPage = $productReviewsIndexPage;
58
    }
59
60
    /**
61
     * @Then I should be able to access product :product
62
     */
63
    public function iShouldBeAbleToAccessProduct(ProductInterface $product)
64
    {
65
        $this->showPage->tryToOpen(['slug' => $product->getSlug()]);
66
67
        Assert::true($this->showPage->isOpen(['slug' => $product->getSlug()]));
68
    }
69
70
    /**
71
     * @Then I should not be able to access product :product
72
     */
73
    public function iShouldNotBeAbleToAccessProduct(ProductInterface $product)
74
    {
75
        $this->showPage->tryToOpen(['slug' => $product->getSlug()]);
76
77
        Assert::false($this->showPage->isOpen(['slug' => $product->getSlug()]));
78
    }
79
80
    /**
81
     * @When /^I check (this product)'s details$/
82
     * @When /^I check (this product)'s details in the ("([^"]+)" locale)$/
83
     * @When I view product :product
84
     * @When I view product :product in the :localeCode locale
85
     */
86
    public function iOpenProductPage(ProductInterface $product, $localeCode = 'en_US')
87
    {
88
        $this->showPage->open(['slug' => $product->getTranslation($localeCode)->getSlug(), '_locale' => $localeCode]);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method getSlug() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Product\Model\ProductTranslation, Sylius\Component\Taxonomy\Model\TaxonTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
89
    }
90
91
    /**
92
     * @When /^I try to check (this product)'s details in the ("([^"]+)" locale)$/
93
     */
94
    public function iTryToOpenProductPage(ProductInterface $product, $localeCode = 'en_US')
95
    {
96
        $this->showPage->tryToOpen([
97
            'slug' => $product->getTranslation($localeCode)->getSlug(),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method getSlug() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Product\Model\ProductTranslation, Sylius\Component\Taxonomy\Model\TaxonTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
98
            '_locale' => $localeCode,
99
        ]);
100
    }
101
102
    /**
103
     * @Then /^I should not be able to view (this product) in the ("([^"]+)" locale)$/
104
     */
105
    public function iShouldNotBeAbleToViewThisProductInLocale(ProductInterface $product, $localeCode = 'en_US')
106
    {
107
        Assert::false(
108
            $this->showPage->isOpen([
109
                'slug' => $product->getTranslation($localeCode)->getSlug(),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method getSlug() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Product\Model\ProductTranslation, Sylius\Component\Taxonomy\Model\TaxonTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
110
                '_locale' => $localeCode,
111
            ])
112
        );
113
    }
114
115
    /**
116
     * @Then I should see the product name :name
117
     */
118
    public function iShouldSeeProductName($name)
119
    {
120
        Assert::same($this->showPage->getName(), $name);
121
    }
122
123
    /**
124
     * @When I open page :url
125
     */
126
    public function iOpenPage($url)
127
    {
128
        $this->showPage->visit($url);
129
    }
130
131
    /**
132
     * @Then I should be on :product product detailed page
133
     * @Then I should still be on product :product page
134
     */
135
    public function iShouldBeOnProductDetailedPage(ProductInterface $product)
136
    {
137
        Assert::true($this->showPage->isOpen(['slug' => $product->getSlug()]));
138
    }
139
140
    /**
141
     * @Then I should (also) see the product attribute :attributeName with value :expectedAttribute
142
     */
143
    public function iShouldSeeTheProductAttributeWithValue($attributeName, $expectedAttribute)
144
    {
145
        Assert::same($this->showPage->getAttributeByName($attributeName), $expectedAttribute);
146
    }
147
148
    /**
149
     * @Then I should (also) see the product attribute :attributeName with date :expectedAttribute
150
     */
151
    public function iShouldSeeTheProductAttributeWithDate($attributeName, $expectedAttribute)
152
    {
153
        Assert::eq(
154
            new \DateTime($this->showPage->getAttributeByName($attributeName)),
155
            new \DateTime($expectedAttribute)
156
        );
157
    }
158
159
    /**
160
     * @Then I should see :count attributes
161
     */
162
    public function iShouldSeeAttributes($count)
163
    {
164
        Assert::same(count($this->getProductAttributes()), (int) $count);
165
    }
166
167
    /**
168
     * @Then the first attribute should be :name
169
     */
170
    public function theFirstAttributeShouldBe($name)
171
    {
172
        $attributes = $this->getProductAttributes();
173
174
        Assert::same(reset($attributes)->getText(), $name);
175
    }
176
177
    /**
178
     * @Then the last attribute should be :name
179
     */
180
    public function theLastAttributeShouldBe($name)
181
    {
182
        $attributes = $this->getProductAttributes();
183
184
        Assert::same(end($attributes)->getText(), $name);
185
    }
186
187
    /**
188
     * @When /^I browse products from (taxon "([^"]+)")$/
189
     */
190
    public function iCheckListOfProductsForTaxon(TaxonInterface $taxon)
191
    {
192
        $this->indexPage->open(['slug' => $taxon->getSlug()]);
193
    }
194
195
    /**
196
     * @When I search for products with name :name
197
     */
198
    public function iSearchForProductsWithName($name)
199
    {
200
        $this->indexPage->search($name);
201
    }
202
203
    /**
204
     * @When I sort products by the lowest price first
205
     */
206
    public function iSortProductsByTheLowestPriceFirst()
207
    {
208
        $this->indexPage->sort('Cheapest first');
209
    }
210
211
    /**
212
     * @When I sort products by the highest price first
213
     */
214
    public function iSortProductsByTheHighestPriceFisrt()
215
    {
216
        $this->indexPage->sort('Most expensive first');
217
    }
218
219
    /**
220
     * @When I sort products alphabetically from a to z
221
     */
222
    public function iSortProductsAlphabeticallyFromAToZ()
223
    {
224
        $this->indexPage->sort('From A to Z');
225
    }
226
227
    /**
228
     * @When I sort products alphabetically from z to a
229
     */
230
    public function iSortProductsAlphabeticallyFromZToA()
231
    {
232
        $this->indexPage->sort('From Z to A');
233
    }
234
235
    /**
236
     * @When I clear filter
237
     */
238
    public function iClearFilter()
239
    {
240
        $this->indexPage->clearFilter();
241
    }
242
243
    /**
244
     * @Then I should see the product :productName
245
     */
246
    public function iShouldSeeProduct($productName)
247
    {
248
        Assert::true($this->indexPage->isProductOnList($productName));
249
    }
250
251
    /**
252
     * @Then I should not see the product :productName
253
     */
254
    public function iShouldNotSeeProduct($productName)
255
    {
256
        Assert::false($this->indexPage->isProductOnList($productName));
257
    }
258
259
    /**
260
     * @Then I should see empty list of products
261
     */
262
    public function iShouldSeeEmptyListOfProducts()
263
    {
264
        Assert::true($this->indexPage->isEmpty());
265
    }
266
267
    /**
268
     * @Then I should see that it is out of stock
269
     */
270
    public function iShouldSeeItIsOutOfStock()
271
    {
272
        Assert::true($this->showPage->isOutOfStock());
273
    }
274
275
    /**
276
     * @Then I should be unable to add it to the cart
277
     */
278
    public function iShouldBeUnableToAddItToTheCart()
279
    {
280
        Assert::false($this->showPage->hasAddToCartButton());
281
    }
282
283
    /**
284
     * @Then the product price should be :price
285
     * @Then I should see the product price :price
286
     */
287
    public function iShouldSeeTheProductPrice($price)
288
    {
289
        Assert::same($this->showPage->getPrice(), $price);
290
    }
291
292
    /**
293
     * @When I set its :optionName to :optionValue
294
     */
295
    public function iSetItsOptionTo($optionName, $optionValue)
296
    {
297
        $this->showPage->selectOption($optionName, $optionValue);
298
    }
299
300
    /**
301
     * @When I select :variantName variant
302
     */
303
    public function iSelectVariant($variantName)
304
    {
305
        $this->showPage->selectVariant($variantName);
306
    }
307
308
    /**
309
     * @Then its current variant should be named :name
310
     */
311
    public function itsCurrentVariantShouldBeNamed($name)
312
    {
313
        Assert::same($this->showPage->getCurrentVariantName(), $name);
314
    }
315
316
    /**
317
     * @Then I should see the product :productName with price :productPrice
318
     */
319
    public function iShouldSeeTheProductWithPrice($productName, $productPrice)
320
    {
321
        Assert::same($this->indexPage->getProductPrice($productName), $productPrice);
322
    }
323
324
    /**
325
     * @Then /^I should be notified that (this product) does not have sufficient stock$/
326
     */
327
    public function iShouldBeNotifiedThatThisProductDoesNotHaveSufficientStock(ProductInterface $product)
328
    {
329
        Assert::true($this->showPage->hasProductOutOfStockValidationMessage($product));
330
    }
331
332
    /**
333
     * @Then /^I should not be notified that (this product) does not have sufficient stock$/
334
     */
335
    public function iShouldNotBeNotifiedThatThisProductDoesNotHaveSufficientStock(ProductInterface $product)
336
    {
337
        Assert::false($this->showPage->hasProductOutOfStockValidationMessage($product));
338
    }
339
340
    /**
341
     * @Then I should see a main image
342
     */
343
    public function iShouldSeeAMainImage()
344
    {
345
        Assert::true($this->showPage->isMainImageDisplayed());
346
    }
347
348
    /**
349
     * @When /^I view (oldest|newest) products from (taxon "([^"]+)")$/
350
     */
351
    public function iViewSortedProductsFromTaxon($sortDirection, TaxonInterface $taxon)
352
    {
353
        $sorting = ['createdAt' => 'oldest' === $sortDirection ? 'asc' : 'desc'];
354
355
        $this->indexPage->open(['slug' => $taxon->getSlug(), 'sorting' => $sorting]);
356
    }
357
358
    /**
359
     * @Then I should see :numberOfProducts products in the list
360
     */
361
    public function iShouldSeeProductsInTheList($numberOfProducts)
362
    {
363
        Assert::same($this->indexPage->countProductsItems(), (int) $numberOfProducts);
364
    }
365
366
    /**
367
     * @Then I should see a product with name :name
368
     */
369
    public function iShouldSeeProductWithName($name)
370
    {
371
        Assert::true($this->indexPage->isProductOnPageWithName($name));
372
    }
373
374
    /**
375
     * @Then the first product on the list should have name :name
376
     */
377
    public function theFirstProductOnTheListShouldHaveName($name)
378
    {
379
        Assert::same($this->indexPage->getFirstProductNameFromList(), $name);
380
    }
381
382
    /**
383
     * @Then the first product on the list should have name :name and price :price
384
     */
385
    public function theFirstProductOnTheListShouldHaveNameAndPrice($name, $price)
386
    {
387
        Assert::same($this->indexPage->getFirstProductNameFromList(), $name);
388
        Assert::same($this->indexPage->getProductPrice($name), $price);
389
    }
390
391
    /**
392
     * @Then the last product on the list should have name :name
393
     */
394
    public function theLastProductOnTheListShouldHaveName($name)
395
    {
396
        Assert::same($this->indexPage->getLastProductNameFromList(), $name);
397
    }
398
399
    /**
400
     * @Then the last product on the list should have name :name and price :price
401
     */
402
    public function theLastProductOnTheListShouldHaveNameAndPrice($name, $price)
403
    {
404
        Assert::same($this->indexPage->getLastProductNameFromList(), $name);
405
        Assert::same($this->indexPage->getProductPrice($name), $price);
406
    }
407
408
    /**
409
     * @Then I should see :count product reviews
410
     */
411
    public function iShouldSeeProductReviews($count)
412
    {
413
        Assert::same($this->showPage->countReviews(), (int) $count);
414
    }
415
416
    /**
417
     * @Then I should see reviews titled :firstReview, :secondReview and :thirdReview
418
     */
419
    public function iShouldSeeReviewsTitled(...$reviews)
420
    {
421
        foreach ($reviews as $review) {
422
            Assert::true(
423
                $this->showPage->hasReviewTitled($review),
424
                sprintf('Product should have review titled "%s" but it does not.', $review)
425
            );
426
        }
427
    }
428
429
    /**
430
     * @Then I should not see review titled :title
431
     */
432
    public function iShouldNotSeeReviewTitled($title)
433
    {
434
        Assert::false($this->showPage->hasReviewTitled($title));
435
    }
436
437
    /**
438
     * @When /^I check (this product)'s reviews$/
439
     */
440
    public function iCheckThisProductSReviews(ProductInterface $product)
441
    {
442
        $this->productReviewsIndexPage->open(['slug' => $product->getSlug()]);
443
    }
444
445
    /**
446
     * @Then /^I should see (\d+) product reviews in the list$/
447
     */
448
    public function iShouldSeeNumberOfProductReviewsInTheList($count)
449
    {
450
        Assert::same($this->productReviewsIndexPage->countReviews(), (int) $count);
451
    }
452
453
    /**
454
     * @Then I should not see review titled :title in the list
455
     */
456
    public function iShouldNotSeeReviewTitledInTheList($title)
457
    {
458
        Assert::false($this->productReviewsIndexPage->hasReviewTitled($title));
459
    }
460
461
    /**
462
     * @Then /^I should be notified that there are no reviews$/
463
     */
464
    public function iShouldBeNotifiedThatThereAreNoReviews()
465
    {
466
        Assert::true($this->productReviewsIndexPage->hasNoReviewsMessage());
467
    }
468
469
    /**
470
     * @Then I should see :rating as its average rating
471
     */
472
    public function iShouldSeeAsItsAverageRating($rating)
473
    {
474
        Assert::same($this->showPage->getAverageRating(), (float) $rating);
475
    }
476
477
    /**
478
     * @Then /^I should(?:| also) see the product association "([^"]+)" with (products "[^"]+" and "[^"]+")$/
479
     */
480
    public function iShouldSeeTheProductAssociationWithProducts($productAssociationName, array $products)
481
    {
482
        Assert::true(
483
            $this->showPage->hasAssociation($productAssociationName),
484
            sprintf('There should be an association named "%s" but it does not.', $productAssociationName)
485
        );
486
487
        foreach ($products as $product) {
488
            $this->assertProductIsInAssociation($product->getName(), $productAssociationName);
489
        }
490
    }
491
492
    /**
493
     * @Then /^average rating of (product "[^"]+") should be (\d+)$/
494
     */
495
    public function thisProductAverageRatingShouldBe(ProductInterface $product, $averageRating)
496
    {
497
        $this->showPage->tryToOpen(['slug' => $product->getSlug()]);
498
        $this->iShouldSeeAsItsAverageRating($averageRating);
499
    }
500
501
    /**
502
     * @Then they should have order like :firstProductName, :secondProductName and :thirdProductName
503
     */
504
    public function theyShouldHaveOrderLikeAnd(...$productNames)
505
    {
506
        Assert::true($this->indexPage->hasProductsInOrder($productNames));
507
    }
508
509
    /**
510
     * @param string $productName
511
     * @param string $productAssociationName
512
     *
513
     * @throws \InvalidArgumentException
514
     */
515
    private function assertProductIsInAssociation($productName, $productAssociationName)
516
    {
517
        Assert::true(
518
            $this->showPage->hasProductInAssociation($productName, $productAssociationName),
519
            sprintf(
520
                'There should be an associated product "%s" under association "%s" but it does not.',
521
                $productName,
522
                $productAssociationName
523
            )
524
        );
525
    }
526
527
    /**
528
     * @return NodeElement[]
529
     *
530
     * @throws \InvalidArgumentException
531
     */
532
    private function getProductAttributes()
533
    {
534
        $attributes = $this->showPage->getAttributes();
535
        Assert::notNull($attributes, 'The product has no attributes.');
536
537
        return $attributes;
538
    }
539
}
540