diff --git a/features/product/managing_product_reviews/editing_product_review.feature b/features/product/managing_product_reviews/editing_product_review.feature index f91f5918d49..e39aa6ed4b8 100644 --- a/features/product/managing_product_reviews/editing_product_review.feature +++ b/features/product/managing_product_reviews/editing_product_review.feature @@ -36,12 +36,12 @@ Feature: Editing product reviews Then I should be notified that it has been successfully edited And this product review rating should be 5 - @ui + @ui @no-api Scenario: Seeing a product's name while editing a product review When I want to modify the "Awesome" product review Then I should be editing review of product "Lamborghini Gallardo Model" - @ui + @ui @no-api Scenario: Seeing a customer's name while editing a product review When I want to modify the "Awesome" product review Then I should see the customer's name "Mike Ross" diff --git a/features/product/managing_product_reviews/filtering_product_reviews.feature b/features/product/managing_product_reviews/filtering_product_reviews.feature index 62b9df88044..047dc30cdb2 100644 --- a/features/product/managing_product_reviews/filtering_product_reviews.feature +++ b/features/product/managing_product_reviews/filtering_product_reviews.feature @@ -11,10 +11,10 @@ Feature: Browsing product reviews And this product has a new review titled "Bad" and rated 1 added by customer "ross@teammike.com" And I am logged in as an administrator - @ui + @ui @api Scenario: Browsing accepted reviews When I want to browse product reviews - And I choose "Accepted" as a status filter + And I choose "accepted" as a status filter And I filter Then I should see a single product review in the list And I should see the product review "Awesome" in the list diff --git a/features/product/managing_product_reviews/recalculating_product_average_rating.feature b/features/product/managing_product_reviews/recalculating_product_average_rating.feature index 0eba93af19d..d3564507ada 100644 --- a/features/product/managing_product_reviews/recalculating_product_average_rating.feature +++ b/features/product/managing_product_reviews/recalculating_product_average_rating.feature @@ -11,7 +11,7 @@ Feature: Recalculating product average rating And this product has a review titled "Not bad" and rated 3 with a comment "Not bad car" added by customer "specter@teamharvey.com" And I am logged in as an administrator - @ui @javascript + @ui @javascript @api Scenario: Product's average rating is correctly recalculated after review's rate change When I want to modify the "Awesome" product review And I choose 5 as its rating @@ -19,7 +19,7 @@ Feature: Recalculating product average rating Then I should be notified that it has been successfully edited And average rating of product "Lamborghini Gallardo Model" should be 4 - @ui + @ui @api Scenario: Product's average rating is correctly recalculated after review's rate change When I delete the "Awesome" product review Then I should be notified that it has been successfully deleted diff --git a/features/product/managing_product_variants/sorting_product_variants_within_product_by_position.feature b/features/product/managing_product_variants/sorting_product_variants_within_product_by_position.feature index ec67bac80d5..15e603f03d9 100644 --- a/features/product/managing_product_variants/sorting_product_variants_within_product_by_position.feature +++ b/features/product/managing_product_variants/sorting_product_variants_within_product_by_position.feature @@ -12,44 +12,46 @@ Feature: Sorting listed product variants from a product by position And this product has also an "Opel Insignia Sedan" variant at position 1 And I am logged in as an administrator - @ui + @ui @api Scenario: Product variants are sorted by position in ascending order by default When I view all variants of the product "Opel Insignia" Then I should see 3 variants in the list And the first variant in the list should have name "Opel Insignia Hatchback" And the last variant in the list should have name "Opel Insignia Sports Tourer" - @ui + @ui @api Scenario: Sorting product variants in descending order When I view all variants of the product "Opel Insignia" And I start sorting variants by position Then the first variant in the list should have name "Opel Insignia Sports Tourer" And the last variant in the list should have name "Opel Insignia Hatchback" - @ui + @ui @api Scenario: New product variant with no position is added as the last one Given the product "Opel Insignia" has also an "Opel Insignia Country Tourer" variant When I view all variants of the product "Opel Insignia" Then I should see 4 variants in the list And the last variant in the list should have name "Opel Insignia Country Tourer" - @ui + @ui @api Scenario: New product variant with position 0 is added as the first one Given the product "Opel Insignia" has also an "Opel Insignia Country Tourer" variant at position 0 When I view all variants of the product "Opel Insignia" Then I should see 4 variants in the list And the first variant in the list should have name "Opel Insignia Country Tourer" - @ui @javascript + @ui @javascript @api Scenario: Setting product variant as the first one in the list When I view all variants of the product "Opel Insignia" And I set the position of "Opel Insignia Sedan" to 0 And I save my new configuration - And the first variant in the list should have name "Opel Insignia Sedan" + And I view all variants of the product "Opel Insignia" again + Then the first variant in the list should have name "Opel Insignia Sedan" - @ui @javascript + @ui @javascript @api Scenario: Setting product variant as the last one in the list When I view all variants of the product "Opel Insignia" And I set the position of "Opel Insignia Sedan" to 7 And I save my new configuration - And the last variant in the list should have name "Opel Insignia Sedan" + And I view all variants of the product "Opel Insignia" again + Then the last variant in the list should have name "Opel Insignia Sedan" diff --git a/src/Sylius/Behat/Context/Api/Admin/BrowsingProductVariantsContext.php b/src/Sylius/Behat/Context/Api/Admin/BrowsingProductVariantsContext.php new file mode 100644 index 00000000000..494c0aed486 --- /dev/null +++ b/src/Sylius/Behat/Context/Api/Admin/BrowsingProductVariantsContext.php @@ -0,0 +1,97 @@ +client->index( + Resources::PRODUCT_VARIANTS, + [ + 'order[position]' => 'desc', + ], + ); + } + + /** + * @When I set the position of :productVariant to :position + */ + public function iSetThePositionOfTo(ProductVariantInterface $productVariant, int $position): void + { + $this->client->buildUpdateRequest(Resources::PRODUCT_VARIANTS, $productVariant->getCode()); + $this->client->updateRequestData(['position' => $position]); + } + + /** + * @When I save my new configuration + */ + public function iSaveMyNewConfiguration(): void + { + $this->client->update(); + } + + /** + * @Then the first variant in the list should have name :variantName + */ + public function theFirstVariantInTheListShouldHaveName(string $variantName): void + { + $variants = $this->responseChecker->getCollection($this->client->getLastResponse()); + + $firstVariant = reset($variants); + + $this->assertProductVariantName($firstVariant['translations']['en_US']['name'], $variantName); + } + + /** + * @Then the last variant in the list should have name :variantName + */ + public function theLastVariantInTheListShouldHaveName(string $variantName): void + { + $variants = $this->responseChecker->getCollection($this->client->getLastResponse()); + + $lastVariant = end($variants); + + $this->assertProductVariantName($lastVariant['translations']['en_US']['name'], $variantName); + } + + private function assertProductVariantName(string $variantName, string $expectedVariantName): void + { + Assert::same( + $variantName, + $expectedVariantName, + sprintf( + 'Expected product variant to have name "%s", but it is named "%s".', + $expectedVariantName, + $variantName, + ), + ); + } +} diff --git a/src/Sylius/Behat/Context/Api/Admin/ManagingProductReviewsContext.php b/src/Sylius/Behat/Context/Api/Admin/ManagingProductReviewsContext.php index 3bfb0a67074..37fde355224 100644 --- a/src/Sylius/Behat/Context/Api/Admin/ManagingProductReviewsContext.php +++ b/src/Sylius/Behat/Context/Api/Admin/ManagingProductReviewsContext.php @@ -18,6 +18,7 @@ use Sylius\Behat\Client\ResponseCheckerInterface; use Sylius\Behat\Context\Api\Resources; use Sylius\Behat\Service\SharedStorageInterface; +use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Review\Model\ReviewInterface; use Webmozart\Assert\Assert; @@ -38,6 +39,22 @@ public function iWantToBrowseProductReviews(): void $this->client->index(Resources::PRODUCT_REVIEWS); } + /** + * @When I choose :status as a status filter + */ + public function iChooseAsStatusFilter(string $status): void + { + $this->client->addFilter('status', $status); + } + + /** + * @When I filter + */ + public function iFilter(): void + { + $this->client->filter(); + } + /** * @When I want to modify the :productReview product review */ @@ -192,6 +209,20 @@ public function iShouldBeNotifiedThatItHasBeenSuccessfullyDeleted(): void ); } + /** + * @Then average rating of product :product should be :expectedRating + */ + public function averageRatingOfProductShouldBe(ProductInterface $product, int $expectedRating): void + { + $averageRating = $this->responseChecker->getValue($this->client->show(Resources::PRODUCTS, (string) $product->getCode()), 'averageRating'); + + Assert::same( + $averageRating, + $expectedRating, + sprintf('Average rating of product %s is not %s', $product->getName(), $expectedRating), + ); + } + private function isItemOnIndex(string $property, string $value): bool { return $this->responseChecker->hasItemWithValue($this->client->index(Resources::PRODUCT_REVIEWS), $property, $value); diff --git a/src/Sylius/Behat/Context/Api/Admin/ManagingProductVariantsContext.php b/src/Sylius/Behat/Context/Api/Admin/ManagingProductVariantsContext.php index 44f5a6f206f..45531fb75fd 100644 --- a/src/Sylius/Behat/Context/Api/Admin/ManagingProductVariantsContext.php +++ b/src/Sylius/Behat/Context/Api/Admin/ManagingProductVariantsContext.php @@ -228,12 +228,13 @@ public function iDoNotWantToHaveShippingRequiredForThisProductVariant(): void /** * @When /^I want to view all variants of (this product)$/ - * @When /^I view(?:| all) variants of the (product "[^"]+")$/ + * @When /^I view(?:| all) variants of the (product "[^"]+")(?:| again)$/ */ public function iWantToViewAllVariantsOfThisProduct(ProductInterface $product): void { $this->client->index(Resources::PRODUCT_VARIANTS); $this->client->addFilter('product', $this->iriConverter->getIriFromResource($product)); + $this->client->addFilter('order[position]', 'asc'); $this->client->filter(); } diff --git a/src/Sylius/Behat/Context/Ui/Admin/BrowsingProductVariantsContext.php b/src/Sylius/Behat/Context/Ui/Admin/BrowsingProductVariantsContext.php index d64bf1deb58..d1cf8ce07ac 100644 --- a/src/Sylius/Behat/Context/Ui/Admin/BrowsingProductVariantsContext.php +++ b/src/Sylius/Behat/Context/Ui/Admin/BrowsingProductVariantsContext.php @@ -87,7 +87,7 @@ public function theProductShouldHaveOnlyOneVariant(ProductInterface $product) /** * @When /^I browse variants of (this product)$/ * @When /^I (?:|want to )view all variants of (this product)$/ - * @When /^I view(?:| all) variants of the (product "[^"]+")$/ + * @When /^I view(?:| all) variants of the (product "[^"]+")(?:| again)$/ */ public function iWantToViewAllVariantsOfThisProduct(ProductInterface $product) { diff --git a/src/Sylius/Behat/Resources/config/services/contexts/api/admin.xml b/src/Sylius/Behat/Resources/config/services/contexts/api/admin.xml index a2029dd0091..6ef0d9482b4 100644 --- a/src/Sylius/Behat/Resources/config/services/contexts/api/admin.xml +++ b/src/Sylius/Behat/Resources/config/services/contexts/api/admin.xml @@ -40,6 +40,11 @@ + + + + + diff --git a/src/Sylius/Behat/Resources/config/suites/api/product/managing_product_variants.yml b/src/Sylius/Behat/Resources/config/suites/api/product/managing_product_variants.yml index bd153ab63fb..1aa96fe2132 100644 --- a/src/Sylius/Behat/Resources/config/suites/api/product/managing_product_variants.yml +++ b/src/Sylius/Behat/Resources/config/suites/api/product/managing_product_variants.yml @@ -40,6 +40,7 @@ default: - sylius.behat.context.api.admin.managing_product_variants - sylius.behat.context.api.admin.response - sylius.behat.context.api.admin.save + - Sylius\Behat\Context\Api\Admin\BrowsingProductVariantsContext - Sylius\Behat\Context\Api\Admin\ChannelPricingLogEntryContext filters: diff --git a/src/Sylius/Bundle/ApiBundle/OpenApi/Documentation/ProductReviewDocumentationModifier.php b/src/Sylius/Bundle/ApiBundle/OpenApi/Documentation/ProductReviewDocumentationModifier.php new file mode 100644 index 00000000000..e48c7d90283 --- /dev/null +++ b/src/Sylius/Bundle/ApiBundle/OpenApi/Documentation/ProductReviewDocumentationModifier.php @@ -0,0 +1,62 @@ +getPaths(); + + $path = sprintf(self::PATH, $this->apiRoute); + $pathItem = $paths->getPath($path); + $operation = $pathItem->getGet(); + + $parameters = $operation->getParameters(); + $parameters = array_filter( + $parameters, + fn (Parameter $parameter) => $parameter->getName() !== 'status' && $parameter->getName() !== 'status[]', + ); + $parameters[] = new Parameter( + name: 'status', + in: 'query', + description: 'Status of product reviews you want to get', + schema: [ + 'type' => 'string', + 'enum' => [ReviewInterface::STATUS_NEW, ReviewInterface::STATUS_ACCEPTED, ReviewInterface::STATUS_REJECTED], + 'nullable' => true, + 'default' => null, + ], + ); + $parameters = array_values($parameters); + + $operation = $operation->withParameters($parameters); + $pathItem = $pathItem->withGet($operation); + $paths->addPath($path, $pathItem); + + return $docs->withPaths($paths); + } +} diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/ProductReview.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/ProductReview.xml index 0fcfabed1fd..0eaac5901fe 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/ProductReview.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/ProductReview.xml @@ -23,6 +23,9 @@ GET /admin/product-reviews + + sylius.api.product_review_status_filter + admin:product_review:read diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/ProductVariant.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/ProductVariant.xml index 2617661fa69..d3c6f1faa5d 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/ProductVariant.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/ProductVariant.xml @@ -57,6 +57,7 @@ GET /admin/product-variants + sylius.api.product_variant_position_filter sylius.api.product_variant_product_filter Sylius\Bundle\ApiBundle\Filter\Doctrine\ProductVariantCatalogPromotionFilter diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/integrations/swagger.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/integrations/swagger.xml index d8a2c60a8f6..a7c1cc92026 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/integrations/swagger.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/integrations/swagger.xml @@ -53,6 +53,11 @@ + + %sylius.security.new_api_route% + + + %sylius.security.new_api_route% diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/serialization/Product.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/serialization/Product.xml index a95a3d961fb..7234af422e4 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/serialization/Product.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/serialization/Product.xml @@ -82,6 +82,7 @@ shop:product:read + admin:product:read shop:product:read diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/services/filters.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/services/filters.xml index 30b875f7421..8c40d29ef27 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/services/filters.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/services/filters.xml @@ -389,5 +389,18 @@ + + + + + + + + + + exact + + + diff --git a/tests/Api/Responses/Expected/admin/product/get_product_response.json b/tests/Api/Responses/Expected/admin/product/get_product_response.json index 391cf64aaff..220145dfe1b 100644 --- a/tests/Api/Responses/Expected/admin/product/get_product_response.json +++ b/tests/Api/Responses/Expected/admin/product/get_product_response.json @@ -42,6 +42,7 @@ "localeCode": "pl_PL" } ], + "averageRating": 0, "images": [ { "@id": "\/api\/v2\/admin\/product-images\/@integer@", diff --git a/tests/Api/Responses/Expected/admin/product/get_products_response.json b/tests/Api/Responses/Expected/admin/product/get_products_response.json index a2ff2590eb3..078e099c8b0 100644 --- a/tests/Api/Responses/Expected/admin/product/get_products_response.json +++ b/tests/Api/Responses/Expected/admin/product/get_products_response.json @@ -23,6 +23,7 @@ ], "reviews": [], "attributes": [], + "averageRating": 0, "images": [], "translations": { "en_US": { @@ -83,6 +84,7 @@ "localeCode": "pl_PL" } ], + "averageRating": 0, "images": [ { "@id": "\/api\/v2\/admin\/product-images\/@integer@", @@ -138,6 +140,7 @@ ], "reviews": [], "attributes": [], + "averageRating": 0, "images": [], "translations": { "en_US": { diff --git a/tests/Api/Responses/Expected/admin/product/post_product_response.json b/tests/Api/Responses/Expected/admin/product/post_product_response.json index 3b8be3c3a15..3ceea8e2fd5 100644 --- a/tests/Api/Responses/Expected/admin/product/post_product_response.json +++ b/tests/Api/Responses/Expected/admin/product/post_product_response.json @@ -26,6 +26,7 @@ "localeCode": null } ], + "averageRating": 0, "images": [], "translations": { "en_US": { diff --git a/tests/Api/Responses/Expected/admin/product/put_product_response.json b/tests/Api/Responses/Expected/admin/product/put_product_response.json index afae963a991..2ad2a9088c2 100644 --- a/tests/Api/Responses/Expected/admin/product/put_product_response.json +++ b/tests/Api/Responses/Expected/admin/product/put_product_response.json @@ -50,6 +50,7 @@ "localeCode": null } ], + "averageRating": 0, "images": [ { "@id": "\/api\/v2\/admin\/product-images\/@integer@", diff --git a/tests/Api/Responses/Expected/admin/product_review/get_product_reviews.json b/tests/Api/Responses/Expected/admin/product_review/get_product_reviews.json index 8b22aad7e6e..d0acb586a09 100644 --- a/tests/Api/Responses/Expected/admin/product_review/get_product_reviews.json +++ b/tests/Api/Responses/Expected/admin/product_review/get_product_reviews.json @@ -69,5 +69,24 @@ "updatedAt": @date@ } ], - "hydra:totalItems": 5 + "hydra:totalItems": 5, + "hydra:search": { + "@type": "hydra:IriTemplate", + "hydra:template": "\/api\/v2\/admin\/product-reviews{?status,status[]}", + "hydra:variableRepresentation": "BasicRepresentation", + "hydra:mapping": [ + { + "@type": "IriTemplateMapping", + "variable": "status", + "property": "status", + "required": false + }, + { + "@type": "IriTemplateMapping", + "variable": "status[]", + "property": "status", + "required": false + } + ] + } } diff --git a/tests/Api/Responses/Expected/admin/product_variant/get_product_variants_response.json b/tests/Api/Responses/Expected/admin/product_variant/get_product_variants_response.json index 202425546fd..70f60dde0f6 100644 --- a/tests/Api/Responses/Expected/admin/product_variant/get_product_variants_response.json +++ b/tests/Api/Responses/Expected/admin/product_variant/get_product_variants_response.json @@ -89,9 +89,15 @@ "hydra:totalItems": 2, "hydra:search": { "@type": "hydra:IriTemplate", - "hydra:template": "\/api\/v2\/admin\/product-variants{?product,product[],catalogPromotion}", + "hydra:template": "\/api\/v2\/admin\/product-variants{?order[position],product,product[],catalogPromotion}", "hydra:variableRepresentation": "BasicRepresentation", "hydra:mapping": [ + { + "@type": "IriTemplateMapping", + "variable": "order[position]", + "property": "position", + "required": false + }, { "@type": "IriTemplateMapping", "variable": "product",