Skip to content

Commit

Permalink
Added validator for empty cart
Browse files Browse the repository at this point in the history
  • Loading branch information
Nenad Stefanovic authored and Nenad Stefanovic committed Dec 8, 2020
1 parent 4f51cf3 commit e7348bb
Show file tree
Hide file tree
Showing 20 changed files with 318 additions and 6 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Shop API for Sylius E-Commerce.",
"license": "MIT",
"require": {
"php": "^7.2",
"php": "^7.3",

"sylius/sylius": "^1.8",
"lexik/jwt-authentication-bundle": "^2.5",
Expand Down
55 changes: 55 additions & 0 deletions spec/Validator/Cart/CartEmptyValidatorSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace spec\Sylius\ShopApiPlugin\Validator\Cart;

use Doctrine\Common\Collections\ArrayCollection;
use PhpSpec\ObjectBehavior;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\ShopApiPlugin\Validator\Constraints\CartEmpty;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

final class CartEmptyValidatorSpec extends ObjectBehavior
{
function let(
OrderRepositoryInterface $repository,
ExecutionContextInterface $context
): void {
$this->beConstructedWith($repository);
$this->initialize($context);
}

function it_add_no_violation_if_cart_is_not_empty(
OrderRepositoryInterface $repository,
OrderInterface $order,
ArrayCollection $collection,
ExecutionContextInterface $context
): void {
$repository->findOneBy(['tokenValue' => 'CART_TOKEN', 'state' => OrderInterface::STATE_CART])->willReturn($order);

$order->getItems()->willReturn($collection);
$collection->isEmpty()->willReturn(false);

$context->addViolation('sylius.shop_api.checkout.cart.empty')->shouldNotBeCalled();

$this->validate('CART_TOKEN', new CartEmpty());
}

function it_add_violation_if_cart_is_empty(
OrderRepositoryInterface $repository,
OrderInterface $order,
ArrayCollection $collection,
ExecutionContextInterface $context
): void {
$repository->findOneBy(['tokenValue' => 'CART_TOKEN', 'state' => OrderInterface::STATE_CART])->willReturn($order);

$order->getItems()->willReturn($collection);
$collection->isEmpty()->willReturn(true);

$context->addViolation('sylius.shop_api.checkout.cart.empty')->shouldBeCalled();

$this->validate('CART_TOKEN', new CartEmpty());
}
}
6 changes: 6 additions & 0 deletions src/Resources/config/services/validators/cart.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,11 @@
<argument type="service" id="sylius.repository.order" />
<tag name="validator.constraint_validator" alias="sylius_shop_api_cart_eligibility_validator" />
</service>

<service id="sylius.shop_api_plugin.validator.cart.cart_empty_validator"
class="Sylius\ShopApiPlugin\Validator\Cart\CartEmptyValidator">
<argument type="service" id="sylius.repository.order" />
<tag name="validator.constraint_validator" alias="sylius_shop_api_cart_empty_validator" />
</service>
</services>
</container>
1 change: 1 addition & 0 deletions src/Resources/config/validation/cart/AddCouponRequest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<option name="message">sylius.shop_api.token.not_null</option>
</constraint>
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartExists" />
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartEmpty" />
</property>
</class>
</constraint-mapping>
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<option name="message">sylius.shop_api.token.not_null</option>
</constraint>
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartExists" />
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartEmpty" />
</property>
<property name="quantity">
<constraint name="Type">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<option name="message">sylius.shop_api.token.not_null</option>
</constraint>
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartExists"/>
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartEmpty"/>
</property>

<property name="countryCode">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<option name="message">sylius.shop_api.token.not_null</option>
</constraint>
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartExists" />
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartEmpty" />
</property>

<property name="shippingAddress">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<option name="message">sylius.shop_api.token.not_null</option>
</constraint>
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartExists"/>
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartEmpty"/>
</property>

<property name="method">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<option name="message">sylius.shop_api.token.not_null</option>
</constraint>
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartExists" />
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartEmpty" />
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartReadyForCheckout" />
<constraint name="Sylius\ShopApiPlugin\Validator\Constraints\CartEligibility" />
</property>
Expand Down
60 changes: 60 additions & 0 deletions src/Validator/Cart/CartEmptyValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace Sylius\ShopApiPlugin\Validator\Cart;

use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\ShopApiPlugin\Validator\Constraints\CartEmpty;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Webmozart\Assert\Assert;

final class CartEmptyValidator extends ConstraintValidator
{
/**
* @var OrderRepositoryInterface
*/
private $_cartRepository;

/**
* CartEmptyValidator constructor.
*
* @param OrderRepositoryInterface $cartRepository
*/
public function __construct(OrderRepositoryInterface $cartRepository)
{
$this->_cartRepository = $cartRepository;
}

/**
* @param mixed $token
* @param Constraint $constraint
*
* @return void
*/
public function validate($token, Constraint $constraint): void
{
Assert::isInstanceOf($constraint, CartEmpty::class);

$cart = $this->_cartRepository->findOneBy(
[
'tokenValue' => $token,
'state' => OrderInterface::STATE_CART
]
);

if ($cart === null) {
return;
}

Assert::isInstanceOf($cart, OrderInterface::class);

if ($cart->getItems()->isEmpty()) {
$this->context->addViolation(
$constraint->emptyCartMessage
);
}
}
}
25 changes: 25 additions & 0 deletions src/Validator/Constraints/CartEmpty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Sylius\ShopApiPlugin\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

final class CartEmpty extends Constraint
{
/** @var string */
public $emptyCartMessage = 'sylius.shop_api.checkout.cart.empty';

/** {@inheritdoc} */
public function getTargets()
{
return self::PROPERTY_CONSTRAINT;
}

/** {@inheritdoc} */
public function validatedBy(): string
{
return 'sylius_shop_api_cart_empty_validator';
}
}
26 changes: 26 additions & 0 deletions tests/Controller/Cart/AddCouponShopApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,30 @@ public function it_does_not_allow_to_add_promotion_code_if_related_promotion_is_

$this->assertResponse($response, 'cart/validation_coupon_not_valid_response', Response::HTTP_BAD_REQUEST);
}

/**
* @test
*/
public function it_does_not_allow_to_add_promotion_code_if_cart_is_empty(): void
{
$this->loadFixturesFromFiles(['channel.yml', 'shop.yml', 'coupon_based_promotion.yml']);

$token = 'SDAOSLEFNWU35H3QLI5325';

/** @var MessageBusInterface $bus */
$bus = $this->get('sylius_shop_api_plugin.command_bus');
$bus->dispatch(new PickupCart($token, 'WEB_GB'));

$data =
<<<JSON
{
"coupon": "BANANAS"
}
JSON;

$this->client->request('PUT', sprintf('/shop-api/carts/%s/coupon', $token), [], [], self::CONTENT_TYPE_HEADER, $data);

$response = $this->client->getResponse();
$this->assertResponse($response, 'checkout/cart_empty_response', Response::HTTP_BAD_REQUEST);
}
}
2 changes: 2 additions & 0 deletions tests/Controller/Cart/ChangeItemQuantityApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Sylius\ShopApiPlugin\Command\Cart\PickupCart;
use Sylius\ShopApiPlugin\Command\Cart\PutSimpleItemToCart;
use Sylius\ShopApiPlugin\Command\Cart\PutVariantBasedConfigurableItemToCart;
use Sylius\ShopApiPlugin\Command\Cart\RemoveItemFromCart;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Messenger\MessageBusInterface;
use Tests\Sylius\ShopApiPlugin\Controller\JsonApiTestCase;
Expand Down Expand Up @@ -214,6 +215,7 @@ public function it_does_not_allow_to_change_quantity_if_cart_item_variant_is_dis
$this->assertResponse($response, 'cart/validation_cart_item_variant_not_eligible_response', Response::HTTP_BAD_REQUEST);
}


private function getFirstOrderItemId(string $orderToken): string
{
/** @var OrderRepositoryInterface $orderRepository */
Expand Down
19 changes: 19 additions & 0 deletions tests/Controller/Cart/EstimateShippingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,23 @@ public function it_calculates_estimated_shipping_cost_based_on_country_and_provi

$this->assertResponse($response, 'cart/estimated_shipping_cost_bases_on_country_and_province_response', Response::HTTP_OK);
}

/**
* @test
*/
public function it_does_not_calculate_estimated_shipping_if_cart_is_empty(): void
{
$this->loadFixturesFromFiles(['shop.yml', 'country.yml', 'shipping.yml']);

$token = 'SDAOSLEFNWU35H3QLI5325';

/** @var MessageBusInterface $bus */
$bus = $this->get('sylius_shop_api_plugin.command_bus');
$bus->dispatch(new PickupCart($token, 'WEB_GB'));

$this->client->request('GET', sprintf('/shop-api/carts/%s/estimated-shipping-cost?countryCode=GB&provinceCode=GB-SCT', $token), [], [], self::CONTENT_TYPE_HEADER);
$response = $this->client->getResponse();

$this->assertResponse($response, 'cart/validation_cart_empty_response', Response::HTTP_BAD_REQUEST);
}
}
33 changes: 33 additions & 0 deletions tests/Controller/Checkout/AddressApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,39 @@ public function it_does_not_allow_to_address_non_existing_order(): void
$this->assertResponse($response, 'cart/validation_cart_not_exists_response', Response::HTTP_BAD_REQUEST);
}

/**
* @test
*/
public function it_does_not_allow_to_address_if_cart_is_empty(): void
{
$this->loadFixturesFromFiles(['shop.yml', 'country.yml']);

$token = 'SDAOSLEFNWU35H3QLI5325';

/** @var MessageBusInterface $bus */
$bus = $this->get('sylius_shop_api_plugin.command_bus');
$bus->dispatch(new PickupCart($token, 'WEB_GB'));

$data =
<<<JSON
{
"shippingAddress": {
"firstName": "Sherlock",
"lastName": "Holmes",
"countryCode": "GB",
"street": "Baker Street 221b",
"city": "London",
"postcode": "NW1",
"provinceName": "Greater London"
}
}
JSON;

$response = $this->address($token, $data);

$this->assertResponse($response, 'checkout/cart_empty_response', Response::HTTP_BAD_REQUEST);
}

/**
* @test
*/
Expand Down
4 changes: 2 additions & 2 deletions tests/Controller/Checkout/ChoosePaymentMethodApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public function it_does_not_allow_to_select_a_payment_method_that_does_not_exist
$bus->dispatch(new ChooseShippingMethod($token, 0, 'DHL'));

$data =
<<<JSON
<<<JSON
{
"method": "NON_EXISTING_PAYMENT"
}
Expand Down Expand Up @@ -131,7 +131,7 @@ public function it_does_not_allow_to_select_a_payment_method_that_not_available_
$bus->dispatch(new ChooseShippingMethod($token, 0, 'DHL'));

$data =
<<<JSON
<<<JSON
{
"method": "NOT_AVAILABLE"
}
Expand Down
Loading

0 comments on commit e7348bb

Please sign in to comment.