diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8679beed..cd7e13b3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: php: [8.0] symfony: [^5.4, ^6.0] sylius: [~1.11.0, ~1.12.0] - node: [14.x] + node: [14.17.x] mysql: [5.7, 8.0] exclude: @@ -124,6 +124,11 @@ jobs: restore-keys: | ${{ runner.os }}-node-${{ matrix.node }}-yarn- + - + name: Copy package.json.dist to package.json + if: matrix.sylius != '' + run: (cd tests/Application && cp package.json.\${{ matrix.sylius }}.dist package.json) + - name: Install JS dependencies run: (cd tests/Application && yarn install) diff --git a/.gitignore b/.gitignore index 5f8e7464..5cefda9b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,12 @@ /tests/Application/yarn.lock +/.phpunit.result.cache /behat.yml /phpspec.yml /phpunit.xml -/.phpunit.result.cache +.phpunit.result.cache + +# Symfony CLI https://symfony.com/doc/current/setup/symfony_server.html#different-php-settings-per-project +/.php-version +/php.ini diff --git a/composer.json b/composer.json index 3130125e..813a3167 100644 --- a/composer.json +++ b/composer.json @@ -6,11 +6,11 @@ "license": "MIT", "require": { "php": "^8.0", - "sylius/sylius": "~1.11.0 || ~1.12.0" + "sylius/sylius": "~1.11.0 || ~1.12.0", + "symfony/webpack-encore-bundle": "^1.12" }, "require-dev": { "ext-json": "*", - "symfony/webpack-encore-bundle": "^1.15", "behat/behat": "^3.6.1", "behat/mink-selenium2-driver": "^1.4", "dmore/behat-chrome-extension": "^1.3", @@ -41,7 +41,8 @@ "friendsofphp/php-cs-fixer": "^3.0", "bitbag/coding-standard": "^1.0", "lchrusciel/api-test-case": "^5.1", - "polishsymfonycommunity/symfony-mocker-container": "^1.0" + "polishsymfonycommunity/symfony-mocker-container": "^1.0", + "php-http/message-factory": "^1.1" }, "conflict": { "symfony/symfony": "4.1.8", diff --git a/features/creating_bundled_product.feature b/features/creating_bundled_product.feature new file mode 100644 index 00000000..be965851 --- /dev/null +++ b/features/creating_bundled_product.feature @@ -0,0 +1,34 @@ +@bundled_product +Feature: Creating a product in store which is a bundle of other products + I want to be able to add bundled product to cart + + Background: + Given the store operates on a single channel in "United States" + And I am logged in as an administrator + And the store has a product "Jack Daniels Gentleman" priced at "$10.00" + And the store has a product "Johny Walker Black" priced at "$10.00" + And the store has a product "Jim Beam Double Oak" priced at "$10.00" + + @ui @javascript + Scenario: Creating a bundled product + When I want to create a new bundled product + And I specify its code as "WHISKEY_PACK" + And I name it "Whiskey double pack" in "English (United States)" + And I set its slug to "whiskey-double-pack" in "English (United States)" + And I set its price to "$10.00" for "United States" channel + And I set its original price to "$20.00" for "United States" channel + And I add product "Johny Walker Black" and "Jack Daniels Gentleman" to the bundle + And I add it + Then I should be notified that it has been successfully created + + @ui @javascript + Scenario: Creating a bundled product with more products + When I want to create a new bundled product + And I specify its code as "WHISKEY_BIG_PACK" + And I name it "Whiskey triple pack" in "English (United States)" + And I set its slug to "whiskey-triple-pack" in "English (United States)" + And I set its price to "$10.00" for "United States" channel + And I set its original price to "$20.00" for "United States" channel + And I add product "Johny Walker Black" and "Jack Daniels Gentleman" and "Jim Beam Double Oak" to the bundle + And I add it + Then I should be notified that it has been successfully created diff --git a/features/having_bundled_product_in_store.feature b/features/having_bundled_product_in_store.feature index d1bdc501..1e9031d2 100644 --- a/features/having_bundled_product_in_store.feature +++ b/features/having_bundled_product_in_store.feature @@ -4,15 +4,37 @@ Feature: Having a product in store which is a bundle of other products Background: Given the store operates on a single channel in "United States" - And the store has locale en_US + And I am a logged in customer + And the store ships everywhere for Free + And the store allows paying Offline @ui - Scenario: Adding a product to cart - Given I am a logged in customer - And the store has a product "Jack Daniels Gentleman" priced at "$10.00" + Scenario: Adding a product bundle to the cart + Given the store has a product "Jack Daniels Gentleman" priced at "$10.00" And the store has a product "Johny Walker Black" priced at "$10.00" And the store has bundled product "Whiskey double pack" priced at "$18.00" which contains "Jack Daniels Gentleman" and "Johny Walker Black" And all store products appear under a main taxonomy Then I added product "Whiskey double pack" to the cart And I should be on my cart summary page And there should be one item in my cart + + @ui + Scenario: Adding a few product bundles to the cart + Given the store has a product "Jim Beam" priced at "$10.00" + And the store has a product "Jim Beam Double Oak" priced at "$10.00" + And the store has bundled product "Jim Beam double pack" priced at "$18.00" which contains "Jim Beam" and "Jim Beam Double Oak" + And all store products appear under a main taxonomy + Then I added product "Jim Beam double pack" to the cart + And I change product "Jim Beam double pack" quantity to 5 in my cart + And I should see "Jim Beam double pack" with quantity 5 in my cart + + @ui + Scenario: Placing an order for a bundled product + Given the store has a product "Jim Beam" priced at "$10.00" + And the store has a product "Jim Beam Double Oak" priced at "$10.00" + And the store has bundled product "Jim Beam double pack" priced at "$18.00" which contains "Jim Beam" and "Jim Beam Double Oak" + Given I have product "Jim Beam double pack" in the cart + And I specified the billing address as "Ankh Morpork", "Frost Alley", "90210", "United States" for "Jon Snow" + And I proceed with "Free" shipping method and "Offline" payment + And I confirm my order + Then I should see the thank you page diff --git a/src/Command/AddProductBundleItemToCartCommand.php b/src/Command/AddProductBundleItemToCartCommand.php index d2a8149d..f2cca7b8 100644 --- a/src/Command/AddProductBundleItemToCartCommand.php +++ b/src/Command/AddProductBundleItemToCartCommand.php @@ -15,18 +15,11 @@ final class AddProductBundleItemToCartCommand { - /** @var ProductBundleItemInterface */ - private $productBundleItem; - - /** @var ProductVariantInterface|null */ - private $productVariant; - - /** @var int|null */ - private $quantity; - - public function __construct(ProductBundleItemInterface $productBundleItem) - { - $this->productBundleItem = $productBundleItem; + public function __construct( + private ProductBundleItemInterface $productBundleItem, + private ?ProductVariantInterface $productVariant = null, + private ?int $quantity = null + ) { $this->productVariant = $productBundleItem->getProductVariant(); $this->quantity = $productBundleItem->getQuantity(); } diff --git a/src/Command/AddProductBundleToCartCommand.php b/src/Command/AddProductBundleToCartCommand.php index 7ef3e44c..6e100b6b 100644 --- a/src/Command/AddProductBundleToCartCommand.php +++ b/src/Command/AddProductBundleToCartCommand.php @@ -12,23 +12,11 @@ final class AddProductBundleToCartCommand implements OrderIdentityAwareInterface, ProductCodeAwareInterface { - /** @var int */ - private $orderId; - - /** @var string */ - private $productCode; - - /** @var int */ - private $quantity; - public function __construct( - int $orderId, - string $productCode, - int $quantity = 1 + private int $orderId, + private string $productCode, + private int $quantity = 1 ) { - $this->orderId = $orderId; - $this->productCode = $productCode; - $this->quantity = $quantity; } public function getOrderId(): int diff --git a/src/Controller/OrderItemController.php b/src/Controller/OrderItemController.php index 48859b53..f13d9e82 100644 --- a/src/Controller/OrderItemController.php +++ b/src/Controller/OrderItemController.php @@ -32,18 +32,6 @@ class OrderItemController extends BaseOrderItemController { - /** @var MessageBusInterface */ - protected $messageBus; - - /** @var OrderRepositoryInterface */ - protected $orderRepository; - - /** @var AddProductBundleToCartDtoFactoryInterface */ - private $addProductBundleToCartDtoFactory; - - /** @var AddProductBundleToCartCommandFactoryInterface */ - private $addProductBundleToCartCommandFactory; - public function __construct( MetadataInterface $metadata, Controller\RequestConfigurationFactoryInterface $requestConfigurationFactory, @@ -62,10 +50,10 @@ public function __construct( Controller\StateMachineInterface $stateMachine, Controller\ResourceUpdateHandlerInterface $resourceUpdateHandler, Controller\ResourceDeleteHandlerInterface $resourceDeleteHandler, - MessageBusInterface $messageBus, - OrderRepositoryInterface $orderRepository, - AddProductBundleToCartDtoFactoryInterface $addProductBundleToCartDtoFactory, - AddProductBundleToCartCommandFactoryInterface $addProductBundleToCartCommandFactory + private MessageBusInterface $messageBus, + private OrderRepositoryInterface $orderRepository, + private AddProductBundleToCartDtoFactoryInterface $addProductBundleToCartDtoFactory, + private AddProductBundleToCartCommandFactoryInterface $addProductBundleToCartCommandFactory ) { parent::__construct( $metadata, @@ -86,11 +74,6 @@ public function __construct( $resourceUpdateHandler, $resourceDeleteHandler ); - - $this->messageBus = $messageBus; - $this->orderRepository = $orderRepository; - $this->addProductBundleToCartDtoFactory = $addProductBundleToCartDtoFactory; - $this->addProductBundleToCartCommandFactory = $addProductBundleToCartCommandFactory; } public function addProductBundleAction(Request $request): ?Response diff --git a/src/Dto/AddProductBundleToCartDto.php b/src/Dto/AddProductBundleToCartDto.php index 6a320a13..fb40c854 100644 --- a/src/Dto/AddProductBundleToCartDto.php +++ b/src/Dto/AddProductBundleToCartDto.php @@ -10,7 +10,6 @@ namespace BitBag\SyliusProductBundlePlugin\Dto; -use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; use BitBag\SyliusProductBundlePlugin\Command\ProductCodeAwareInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductInterface; use Doctrine\Common\Collections\ArrayCollection; @@ -19,30 +18,14 @@ final class AddProductBundleToCartDto implements AddProductBundleToCartDtoInterface, ProductCodeAwareInterface { - /** @var OrderInterface */ - private $cart; + private ArrayCollection $productBundleItems; - /** @var OrderItemInterface */ - private $cartItem; - - /** @var ProductInterface */ - private $product; - - /** @var ArrayCollection */ - private $productBundleItems; - - /** - * @param AddProductBundleItemToCartCommand[] $productBundleItems - */ public function __construct( - OrderInterface $cart, - OrderItemInterface $cartItem, - ProductInterface $product, + private OrderInterface $cart, + private OrderItemInterface $cartItem, + private ProductInterface $product, array $productBundleItems ) { - $this->cart = $cart; - $this->cartItem = $cartItem; - $this->product = $product; $this->productBundleItems = new ArrayCollection($productBundleItems); } diff --git a/src/Dto/Api/AddProductBundleToCartDto.php b/src/Dto/Api/AddProductBundleToCartDto.php index dd8d2e85..4d96771c 100644 --- a/src/Dto/Api/AddProductBundleToCartDto.php +++ b/src/Dto/Api/AddProductBundleToCartDto.php @@ -14,19 +14,11 @@ final class AddProductBundleToCartDto implements OrderTokenValueAwareInterface { - /** @var string|null */ - private $orderTokenValue; - - /** @var string */ - private $productCode; - - /** @var int */ - private $quantity; - - public function __construct(string $productCode, int $quantity = 1) - { - $this->productCode = $productCode; - $this->quantity = $quantity; + public function __construct( + private string $productCode, + private int $quantity = 1, + private ?string $orderTokenValue = null + ) { } public function getOrderTokenValue(): ?string diff --git a/src/Factory/AddProductBundleToCartDtoFactory.php b/src/Factory/AddProductBundleToCartDtoFactory.php index b53e2012..b5277347 100644 --- a/src/Factory/AddProductBundleToCartDtoFactory.php +++ b/src/Factory/AddProductBundleToCartDtoFactory.php @@ -20,12 +20,9 @@ final class AddProductBundleToCartDtoFactory implements AddProductBundleToCartDtoFactoryInterface { - /** @var AddProductBundleItemToCartCommandFactoryInterface */ - private $addProductBundleItemToCartCommandFactory; - - public function __construct(AddProductBundleItemToCartCommandFactoryInterface $addProductBundleItemToCartCommandFactory) - { - $this->addProductBundleItemToCartCommandFactory = $addProductBundleItemToCartCommandFactory; + public function __construct( + private AddProductBundleItemToCartCommandFactoryInterface $addProductBundleItemToCartCommandFactory + ) { } public function createNew( diff --git a/src/Factory/OrderItemFactory.php b/src/Factory/OrderItemFactory.php index 38978397..b959ff98 100644 --- a/src/Factory/OrderItemFactory.php +++ b/src/Factory/OrderItemFactory.php @@ -18,12 +18,9 @@ final class OrderItemFactory implements OrderItemFactoryInterface { - /** @var CartItemFactoryInterface */ - private $decoratedFactory; - - public function __construct(CartItemFactoryInterface $decoratedFactory) - { - $this->decoratedFactory = $decoratedFactory; + public function __construct( + private CartItemFactoryInterface $decoratedFactory + ) { } public function createNew(): OrderItemInterface diff --git a/src/Factory/ProductBundleOrderItemFactory.php b/src/Factory/ProductBundleOrderItemFactory.php index cbc883cd..c54bc5b5 100644 --- a/src/Factory/ProductBundleOrderItemFactory.php +++ b/src/Factory/ProductBundleOrderItemFactory.php @@ -16,12 +16,9 @@ final class ProductBundleOrderItemFactory implements ProductBundleOrderItemFactoryInterface { - /** @var FactoryInterface */ - private $decoratedFactory; - - public function __construct(FactoryInterface $decoratedFactory) - { - $this->decoratedFactory = $decoratedFactory; + public function __construct( + private FactoryInterface $decoratedFactory + ) { } public function createNew(): ProductBundleOrderItemInterface diff --git a/src/Factory/ProductFactory.php b/src/Factory/ProductFactory.php index 65e6eef5..08507701 100644 --- a/src/Factory/ProductFactory.php +++ b/src/Factory/ProductFactory.php @@ -18,16 +18,10 @@ final class ProductFactory implements ProductFactoryInterface { - /** @var DecoratedProductFactoryInterface */ - private $decoratedFactory; - - /** @var FactoryInterface */ - private $productBundleFactory; - - public function __construct(DecoratedProductFactoryInterface $decoratedFactory, FactoryInterface $productBundleFactory) - { - $this->decoratedFactory = $decoratedFactory; - $this->productBundleFactory = $productBundleFactory; + public function __construct( + private DecoratedProductFactoryInterface $decoratedFactory, + private FactoryInterface $productBundleFactory + ) { } public function createWithVariantAndBundle(): BaseProductInterface diff --git a/src/Handler/AddProductBundleToCartHandler.php b/src/Handler/AddProductBundleToCartHandler.php index 5ae0af0c..e0caed6d 100644 --- a/src/Handler/AddProductBundleToCartHandler.php +++ b/src/Handler/AddProductBundleToCartHandler.php @@ -21,23 +21,11 @@ final class AddProductBundleToCartHandler implements MessageHandlerInterface { - /** @var OrderRepositoryInterface */ - private $orderRepository; - - /** @var ProductRepositoryInterface */ - private $productRepository; - - /** @var CartProcessorInterface */ - private $cartProcessor; - public function __construct( - OrderRepositoryInterface $orderRepository, - ProductRepositoryInterface $productRepository, - CartProcessorInterface $cartItemProcessor + private OrderRepositoryInterface $orderRepository, + private ProductRepositoryInterface $productRepository, + private CartProcessorInterface $cartProcessor ) { - $this->orderRepository = $orderRepository; - $this->productRepository = $productRepository; - $this->cartProcessor = $cartItemProcessor; } public function __invoke(AddProductBundleToCartCommand $addProductBundleToCartCommand): void diff --git a/src/Handler/AddProductBundleToCartHandler/CartProcessor.php b/src/Handler/AddProductBundleToCartHandler/CartProcessor.php index bb2ffe32..9cb4dd87 100644 --- a/src/Handler/AddProductBundleToCartHandler/CartProcessor.php +++ b/src/Handler/AddProductBundleToCartHandler/CartProcessor.php @@ -21,28 +21,12 @@ final class CartProcessor implements CartProcessorInterface { - /** @var OrderItemQuantityModifierInterface */ - private $orderItemQuantityModifier; - - /** @var ProductBundleOrderItemFactoryInterface */ - private $productBundleOrderItemFactory; - - /** @var OrderModifierInterface */ - private $orderModifier; - - /** @var OrderItemFactoryInterface */ - private $cartItemFactory; - public function __construct( - OrderItemQuantityModifierInterface $orderItemQuantityModifier, - ProductBundleOrderItemFactoryInterface $productBundleOrderItemFactory, - OrderModifierInterface $orderModifier, - OrderItemFactoryInterface $cartItemFactory + private OrderItemQuantityModifierInterface $orderItemQuantityModifier, + private ProductBundleOrderItemFactoryInterface $productBundleOrderItemFactory, + private OrderModifierInterface $orderModifier, + private OrderItemFactoryInterface $cartItemFactory ) { - $this->orderItemQuantityModifier = $orderItemQuantityModifier; - $this->productBundleOrderItemFactory = $productBundleOrderItemFactory; - $this->orderModifier = $orderModifier; - $this->cartItemFactory = $cartItemFactory; } public function process( diff --git a/src/Resources/views/Admin/Form/productBundleItem.html.twig b/src/Resources/views/Admin/Form/productBundleItem.html.twig index 12786be3..60d9deb8 100644 --- a/src/Resources/views/Admin/Form/productBundleItem.html.twig +++ b/src/Resources/views/Admin/Form/productBundleItem.html.twig @@ -36,7 +36,7 @@ {% if prototype is defined and allow_add %} - + {{ button_add_label|trans }} @@ -49,7 +49,7 @@ {% apply spaceless %} - {{ form_row(form.productVariant, {'remote_url': path('bitbag_product_bundle_admin_ajax_product_variants_by_phrase'), 'remote_criteria_type': 'contains', 'remote_criteria_name': 'phrase', 'load_edit_url': path('bitbag_product_bundle_admin_ajax_product_variants_by_codes')}) }} +
{{ form_row(form.productVariant, {'remote_url': path('bitbag_product_bundle_admin_ajax_product_variants_by_phrase'), 'remote_criteria_type': 'contains', 'remote_criteria_name': 'phrase', 'load_edit_url': path('bitbag_product_bundle_admin_ajax_product_variants_by_codes')}) }}
{{ form_row(form.quantity) }} diff --git a/src/Twig/Extension/ProductBundleOrderItemExtension.php b/src/Twig/Extension/ProductBundleOrderItemExtension.php index d43eb70f..fcb3fd2f 100644 --- a/src/Twig/Extension/ProductBundleOrderItemExtension.php +++ b/src/Twig/Extension/ProductBundleOrderItemExtension.php @@ -19,16 +19,10 @@ final class ProductBundleOrderItemExtension extends AbstractExtension { - /** @var RepositoryInterface */ - private $productBundleOrderItemRepository; - - /** @var Environment */ - private $twig; - - public function __construct(RepositoryInterface $productBundleOrderItemRepository, Environment $twig) - { - $this->productBundleOrderItemRepository = $productBundleOrderItemRepository; - $this->twig = $twig; + public function __construct( + private RepositoryInterface $productBundleOrderItemRepository, + private Environment $twig + ) { } public function getFunctions(): array diff --git a/src/Validator/HasAvailableProductBundleValidator.php b/src/Validator/HasAvailableProductBundleValidator.php index 9aa902d2..c22a119e 100644 --- a/src/Validator/HasAvailableProductBundleValidator.php +++ b/src/Validator/HasAvailableProductBundleValidator.php @@ -25,23 +25,11 @@ final class HasAvailableProductBundleValidator extends ConstraintValidator { - /** @var ProductRepositoryInterface */ - private $productRepository; - - /** @var OrderRepositoryInterface */ - private $orderRepository; - - /** @var AvailabilityCheckerInterface */ - private $availabilityChecker; - public function __construct( - ProductRepositoryInterface $productRepository, - OrderRepositoryInterface $orderRepository, - AvailabilityCheckerInterface $availabilityChecker + private ProductRepositoryInterface $productRepository, + private OrderRepositoryInterface $orderRepository, + private AvailabilityCheckerInterface $availabilityChecker ) { - $this->productRepository = $productRepository; - $this->orderRepository = $orderRepository; - $this->availabilityChecker = $availabilityChecker; } /** diff --git a/src/Validator/HasExistingCartValidator.php b/src/Validator/HasExistingCartValidator.php index 94861df2..75d39d0c 100644 --- a/src/Validator/HasExistingCartValidator.php +++ b/src/Validator/HasExistingCartValidator.php @@ -19,12 +19,9 @@ final class HasExistingCartValidator extends ConstraintValidator { - /** @var OrderRepositoryInterface */ - private $orderRepository; - - public function __construct(OrderRepositoryInterface $orderRepository) - { - $this->orderRepository = $orderRepository; + public function __construct( + private OrderRepositoryInterface $orderRepository + ) { } /** diff --git a/src/Validator/HasProductBundleValidator.php b/src/Validator/HasProductBundleValidator.php index 0b53ee55..58a4c4cb 100644 --- a/src/Validator/HasProductBundleValidator.php +++ b/src/Validator/HasProductBundleValidator.php @@ -20,12 +20,9 @@ final class HasProductBundleValidator extends ConstraintValidator { - /** @var ProductRepositoryInterface */ - private $productRepository; - - public function __construct(ProductRepositoryInterface $productRepository) - { - $this->productRepository = $productRepository; + public function __construct( + private ProductRepositoryInterface $productRepository + ) { } /** diff --git a/src/Validator/Sequentially.php b/src/Validator/Sequentially.php index c92c6c42..ce95d6f9 100644 --- a/src/Validator/Sequentially.php +++ b/src/Validator/Sequentially.php @@ -14,10 +14,7 @@ final class Sequentially extends Composite { - /** @var array */ - public $constraints = []; - - public function __construct(?array $constraints = null) + public function __construct(public ?array $constraints = null) { parent::__construct($constraints ?? []); } diff --git a/src/Validator/SequentiallyValidator.php b/src/Validator/SequentiallyValidator.php index de83a512..057eeb58 100644 --- a/src/Validator/SequentiallyValidator.php +++ b/src/Validator/SequentiallyValidator.php @@ -28,9 +28,11 @@ public function validate(mixed $value, Constraint $constraint): void $originalCount = $validator->getViolations()->count(); - foreach ($constraint->constraints as $c) { - if ($originalCount !== $validator->validate($value, $c)->getViolations()->count()) { - break; + if (is_iterable($constraint->constraints)) { + foreach ($constraint->constraints as $c) { + if ($originalCount !== $validator->validate($value, $c)->getViolations()->count()) { + break; + } } } } diff --git a/tests/Application/.gitignore b/tests/Application/.gitignore index 8ad1225e..bc600a8c 100644 --- a/tests/Application/.gitignore +++ b/tests/Application/.gitignore @@ -1,4 +1,5 @@ /public/assets +/public/build /public/css /public/js /public/media/* diff --git a/tests/Application/config/bundles.php b/tests/Application/config/bundles.php index 414b2e59..18e508fd 100644 --- a/tests/Application/config/bundles.php +++ b/tests/Application/config/bundles.php @@ -1,11 +1,5 @@ ['all' => true], Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['test' => true], Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['test' => true], + Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], + Sylius\Calendar\SyliusCalendarBundle::class => ['all' => true], + BabDev\PagerfantaBundle\BabDevPagerfantaBundle::class => ['all' => true], + SyliusLabs\Polyfill\Symfony\Security\Bundle\SyliusLabsPolyfillSymfonySecurityBundle::class => ['all' => true], ]; diff --git a/tests/Application/config/packages/assets.yaml b/tests/Application/config/packages/assets.yaml new file mode 100644 index 00000000..a7b133d0 --- /dev/null +++ b/tests/Application/config/packages/assets.yaml @@ -0,0 +1,11 @@ +framework: + assets: + packages: + shop: + json_manifest_path: '%kernel.project_dir%/public/build/shop/manifest.json' + admin: + json_manifest_path: '%kernel.project_dir%/public/build/admin/manifest.json' + product_bundle_shop: + json_manifest_path: '%kernel.project_dir%/public/build/bitbag/productBundle/shop/manifest.json' + product_bundle_admin: + json_manifest_path: '%kernel.project_dir%/public/build/bitbag/productBundle/admin/manifest.json' diff --git a/tests/Application/config/packages/webpack_encore.yaml b/tests/Application/config/packages/webpack_encore.yaml index 80beb76c..33196059 100644 --- a/tests/Application/config/packages/webpack_encore.yaml +++ b/tests/Application/config/packages/webpack_encore.yaml @@ -3,5 +3,5 @@ webpack_encore: builds: shop: '%kernel.project_dir%/public/build/shop' admin: '%kernel.project_dir%/public/build/admin' - cs_shop: '%kernel.project_dir%/public/build/bitbag/cs/shop' - cs_admin: '%kernel.project_dir%/public/build/bitbag/cs/admin' + product_bundle_shop: '%kernel.project_dir%/public/build/bitbag/productBundle/shop' + product_bundle_admin: '%kernel.project_dir%/public/build/bitbag/productBundle/admin' diff --git a/tests/Application/config/sylius/1.11/bundles.php b/tests/Application/config/sylius/1.11/bundles.php index aa8974a9..b8da49fb 100644 --- a/tests/Application/config/sylius/1.11/bundles.php +++ b/tests/Application/config/sylius/1.11/bundles.php @@ -1,26 +1,7 @@ ['all' => true], - Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], ]; - -if (class_exists('BabDev\PagerfantaBundle\BabDevPagerfantaBundle')) { - $bundles[BabDev\PagerfantaBundle\BabDevPagerfantaBundle::class] = ['all' => true]; -} -if (class_exists('SyliusLabs\Polyfill\Symfony\Security\Bundle\SyliusLabsPolyfillSymfonySecurityBundle')) { - $bundles[SyliusLabs\Polyfill\Symfony\Security\Bundle\SyliusLabsPolyfillSymfonySecurityBundle::class] = ['all' => true]; -} -if (class_exists('Sylius\Calendar\SyliusCalendarBundle')) { - $bundles[Sylius\Calendar\SyliusCalendarBundle::class] = ['all' => true]; -} - -return $bundles; diff --git a/tests/Application/config/sylius/1.12/bundles.php b/tests/Application/config/sylius/1.12/bundles.php index 82a796e6..5f922a2d 100644 --- a/tests/Application/config/sylius/1.12/bundles.php +++ b/tests/Application/config/sylius/1.12/bundles.php @@ -1,17 +1,7 @@ ['all' => true], - Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], - Sylius\Calendar\SyliusCalendarBundle::class => ['all' => true], - BabDev\PagerfantaBundle\BabDevPagerfantaBundle::class => ['all' => true], - SyliusLabs\Polyfill\Symfony\Security\Bundle\SyliusLabsPolyfillSymfonySecurityBundle::class => ['all' => true], ]; diff --git a/tests/Application/package.json.~1.11.0.dist b/tests/Application/package.json.~1.11.0.dist new file mode 100644 index 00000000..36e6c1f0 --- /dev/null +++ b/tests/Application/package.json.~1.11.0.dist @@ -0,0 +1,41 @@ +{ + "dependencies": { + "@babel/polyfill": "^7.0.0", + "chart.js": "^3.7.1", + "jquery": "^3.5.0", + "jquery.dirtyforms": "^2.0.0", + "lightbox2": "^2.9.0", + "semantic-ui-css": "^2.2.0", + "slick-carousel": "^1.8.1" + }, + "devDependencies": { + "@symfony/webpack-encore": "^1.6.1", + "babel-core": "^6.26.3", + "babel-plugin-external-helpers": "^6.22.0", + "babel-plugin-module-resolver": "^3.1.1", + "babel-plugin-transform-object-rest-spread": "^6.26.0", + "babel-preset-env": "^1.7.0", + "babel-register": "^6.26.0", + "dedent": "^0.7.0", + "eslint": "^4.19.1", + "eslint-config-airbnb-base": "^12.1.0", + "eslint-import-resolver-babel-module": "^4.0.0", + "eslint-plugin-import": "^2.11.0", + "merge-stream": "^1.0.0", + "sass": "^1.39.2", + "sass-loader": "^12.1.0" + }, + "scripts": { + "dev": "yarn encore dev", + "watch": "yarn encore dev --watch", + "prod": "yarn encore prod", + "lint": "yarn lint:js", + "lint:js": "eslint gulpfile.babel.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Sylius/Sylius.git" + }, + "author": "Paweł Jędrzejewski", + "license": "MIT" +} diff --git a/tests/Application/package.json b/tests/Application/package.json.~1.12.0.dist similarity index 100% rename from tests/Application/package.json rename to tests/Application/package.json.~1.12.0.dist diff --git a/tests/Application/templates/bundles/SyliusAdminBundle/Layout/_logo.html.twig b/tests/Application/templates/bundles/SyliusAdminBundle/Layout/_logo.html.twig new file mode 100644 index 00000000..1d9fa7d0 --- /dev/null +++ b/tests/Application/templates/bundles/SyliusAdminBundle/Layout/_logo.html.twig @@ -0,0 +1,5 @@ + +
+ +
+
diff --git a/tests/Application/templates/bundles/SyliusAdminBundle/_scripts.html.twig b/tests/Application/templates/bundles/SyliusAdminBundle/_scripts.html.twig new file mode 100644 index 00000000..54be86fa --- /dev/null +++ b/tests/Application/templates/bundles/SyliusAdminBundle/_scripts.html.twig @@ -0,0 +1,2 @@ +{{ encore_entry_script_tags('admin-entry', null, 'admin') }} +{{ encore_entry_script_tags('bitbag-productBundle-admin', null, 'product_bundle_admin') }} diff --git a/tests/Application/templates/bundles/SyliusAdminBundle/_styles.html.twig b/tests/Application/templates/bundles/SyliusAdminBundle/_styles.html.twig new file mode 100644 index 00000000..d87343da --- /dev/null +++ b/tests/Application/templates/bundles/SyliusAdminBundle/_styles.html.twig @@ -0,0 +1,2 @@ +{{ encore_entry_link_tags('admin-entry', null, 'admin') }} +{{ encore_entry_link_tags('bitbag-productBundle-admin', null, 'product_bundle_admin') }} diff --git a/tests/Application/templates/bundles/SyliusShopBundle/Layout/Header/_logo.html.twig b/tests/Application/templates/bundles/SyliusShopBundle/Layout/Header/_logo.html.twig new file mode 100644 index 00000000..84b8df56 --- /dev/null +++ b/tests/Application/templates/bundles/SyliusShopBundle/Layout/Header/_logo.html.twig @@ -0,0 +1,5 @@ +
+ + Sylius logo + +
diff --git a/tests/Application/templates/bundles/SyliusShopBundle/_scripts.html.twig b/tests/Application/templates/bundles/SyliusShopBundle/_scripts.html.twig new file mode 100644 index 00000000..278c091e --- /dev/null +++ b/tests/Application/templates/bundles/SyliusShopBundle/_scripts.html.twig @@ -0,0 +1,2 @@ +{{ encore_entry_script_tags('shop-entry', null, 'shop') }} +{{ encore_entry_script_tags('bitbag-productBundle-shop', null, 'product_bundle_shop') }} diff --git a/tests/Application/templates/bundles/SyliusShopBundle/_styles.html.twig b/tests/Application/templates/bundles/SyliusShopBundle/_styles.html.twig new file mode 100644 index 00000000..5365a60e --- /dev/null +++ b/tests/Application/templates/bundles/SyliusShopBundle/_styles.html.twig @@ -0,0 +1,2 @@ +{{ encore_entry_link_tags('shop-entry', null, 'shop') }} +{{ encore_entry_link_tags('bitbag-productBundle-shop', null, 'product_bundle_shop') }} diff --git a/tests/Application/webpack.config.js b/tests/Application/webpack.config.js index be0fe917..8bbcb1a9 100644 --- a/tests/Application/webpack.config.js +++ b/tests/Application/webpack.config.js @@ -1,6 +1,6 @@ const path = require('path'); const Encore = require('@symfony/webpack-encore'); -const [bitbagCsShop, bitbagCsAdmin] = require('../../webpack.config.js'); +const [bitbagProductBundleShop, bitbagProductBundleAdmin] = require('../../webpack.config.js'); const syliusBundles = path.resolve(__dirname, '../../vendor/sylius/sylius/src/Sylius/Bundle/'); const uiBundleScripts = path.resolve(syliusBundles, 'UiBundle/Resources/private/js/'); @@ -44,4 +44,4 @@ adminConfig.resolve.alias['chart.js/dist/Chart.min'] = path.resolve(__dirname, ' adminConfig.externals = Object.assign({}, adminConfig.externals, {window: 'window', document: 'document'}); adminConfig.name = 'admin'; -module.exports = [shopConfig, adminConfig, bitbagCsShop, bitbagCsAdmin]; +module.exports = [shopConfig, adminConfig, bitbagProductBundleShop, bitbagProductBundleAdmin]; diff --git a/tests/Behat/Context/Setup/ProductBundleContext.php b/tests/Behat/Context/Setup/ProductBundleContext.php index e36ae923..cbd157f6 100644 --- a/tests/Behat/Context/Setup/ProductBundleContext.php +++ b/tests/Behat/Context/Setup/ProductBundleContext.php @@ -29,58 +29,18 @@ final class ProductBundleContext implements Context { - /** @var SharedStorageInterface */ - private $sharedStorage; - - /** @var FactoryInterface */ - private $taxonFactory; - - /** @var ProductRepositoryInterface */ - private $productRepository; - - /** @var FactoryInterface */ - private $productTaxonFactory; - - /** @var EntityManagerInterface */ - private $productTaxonManager; - - /** @var ProductFactory */ - private $productFactory; - - /** @var FactoryInterface */ - private $productBundleItemFactory; - - /** @var FactoryInterface */ - private $channelPricingFactory; - - /** @var ProductVariantResolverInterface */ - private $productVariantResolver; - - /** @var SlugGeneratorInterface */ - private $slugGenerator; - public function __construct( - SharedStorageInterface $sharedStorage, - FactoryInterface $taxonFactory, - ProductRepositoryInterface $productRepository, - FactoryInterface $productTaxonFactory, - EntityManagerInterface $productTaxonManager, - ProductFactory $productFactory, - FactoryInterface $productBundleItemFactory, - FactoryInterface $channelPricingFactory, - ProductVariantResolverInterface $productVariantResolver, - SlugGeneratorInterface $slugGenerator + private SharedStorageInterface $sharedStorage, + private FactoryInterface $taxonFactory, + private ProductRepositoryInterface $productRepository, + private FactoryInterface $productTaxonFactory, + private EntityManagerInterface $productTaxonManager, + private ProductFactory $productFactory, + private FactoryInterface $productBundleItemFactory, + private FactoryInterface $channelPricingFactory, + private ProductVariantResolverInterface $productVariantResolver, + private SlugGeneratorInterface $slugGenerator ) { - $this->sharedStorage = $sharedStorage; - $this->taxonFactory = $taxonFactory; - $this->productRepository = $productRepository; - $this->productTaxonFactory = $productTaxonFactory; - $this->productTaxonManager = $productTaxonManager; - $this->productFactory = $productFactory; - $this->productBundleItemFactory = $productBundleItemFactory; - $this->channelPricingFactory = $channelPricingFactory; - $this->productVariantResolver = $productVariantResolver; - $this->slugGenerator = $slugGenerator; } /** diff --git a/tests/Behat/Context/Ui/ProductBundleContext.php b/tests/Behat/Context/Ui/ProductBundleContext.php new file mode 100644 index 00000000..85f4429d --- /dev/null +++ b/tests/Behat/Context/Ui/ProductBundleContext.php @@ -0,0 +1,92 @@ +createBundledProductPage->open(); + } + + /** + * @When I specify its code as :code + */ + public function iSpecifyItsCode(string $code): void + { + $this->createBundledProductPage->specifyCode($code); + } + + /** + * @When I name it :name in :language + */ + public function iRenameItToIn(?string $name = null, ?string $language = null): void + { + if (null !== $name && null !== $language) { + $this->createBundledProductPage->nameItIn($name, $language); + } + } + + /** + * @When I set its slug to :slug + * @When I set its slug to :slug in :language + */ + public function iSetItsSlugToIn(?string $slug = null, $language = 'en_US') + { + $this->createBundledProductPage->specifySlugIn($slug, $language); + } + + /** + * @When /^I set its(?:| default) price to "(?:€|£|\$)([^"]+)" for ("([^"]+)" channel)$/ + */ + public function iSetItsPriceTo(string $price, ChannelInterface $channel) + { + $this->createBundledProductPage->specifyPrice($channel, $price); + } + + /** + * @When /^I set its original price to "(?:€|£|\$)([^"]+)" for ("([^"]+)" channel)$/ + */ + public function iSetItsOriginalPriceTo(int $originalPrice, ChannelInterface $channel) + { + $this->createBundledProductPage->specifyOriginalPrice($channel, $originalPrice); + } + + /** + * @When I add it + */ + public function iAddIt() + { + $this->createBundledProductPage->create(); + } + + /** + * @When I add product :productName to the bundle + * @When I add product :firstProductName and :secondProductName to the bundle + * @When I add product :firstProductName and :secondProductName and :thirdProductName to the bundle + */ + public function iAddProductsToBundledProduct(...$productsNames) + { + $this->createBundledProductPage->addProductsToBundle($productsNames); + } +} diff --git a/tests/Behat/Page/Admin/CreateBundledProductPage.php b/tests/Behat/Page/Admin/CreateBundledProductPage.php new file mode 100644 index 00000000..5d83e714 --- /dev/null +++ b/tests/Behat/Page/Admin/CreateBundledProductPage.php @@ -0,0 +1,121 @@ +getDocument()->fillField('Code', $code); + } + + public function nameItIn(string $name, string $localeCode): void + { + $this->clickTabIfItsNotActive('details'); + $this->activateLanguageTab($localeCode); + $this->getElement('name', ['%locale%' => $localeCode])->setValue($name); + } + + public function specifySlugIn(?string $slug, string $locale): void + { + $this->activateLanguageTab($locale); + + $this->getElement('slug', ['%locale%' => $locale])->setValue($slug); + } + + public function activateLanguageTab(string $locale): void + { + if (DriverHelper::isNotJavascript($this->getDriver())) { + return; + } + + $languageTabTitle = $this->getElement('language_tab', ['%locale%' => $locale]); + if (!$languageTabTitle->hasClass('active')) { + $languageTabTitle->click(); + } + } + + public function specifyPrice(ChannelInterface $channel, string $price): void + { + $this->getElement('price', ['%channelCode%' => $channel->getCode()])->setValue($price); + } + + public function specifyOriginalPrice(ChannelInterface $channel, int $originalPrice): void + { + $this->getElement('original_price', ['%channelCode%' => $channel->getCode()])->setValue($originalPrice); + } + + public function addProductsToBundle(array $productsNames): void + { + $this->clickTabIfItsNotActive('bundle'); + + $productCounter = 0; + + foreach ($productsNames as $productName) { + $addSelector = $this->getElement('add_product_to_bundle_button'); + $addSelector->click(); + $addSelector->waitFor(5, fn () => $this->hasElement('product_selector_dropdown')); + + $dropdown = $this->getLastProductAutocomplete(); + $dropdown->click(); + $dropdown->waitFor(5, fn () => $this->hasElement('product_selector_dropdown_item')); + + $item = $this->getElement('product_selector_dropdown_item', [ + '%item%' => $productName, + ]); + $item->click(); + + $this->getElement('product_selector_quantity', ['%productCounter%' => $productCounter])->setValue('1'); + + ++$productCounter; + } + } + + protected function getDefinedElements(): array + { + return array_merge(parent::getDefinedElements(), [ + 'product_selector_quantity' => '#sylius_product_productBundle_productBundleItems_%productCounter%_quantity', + 'product_selector_dropdown_item' => '#add_product_to_bundle_autocomplete > div > div > div.menu.transition.visible > div.item:contains("%item%")', + 'product_selector_dropdown' => '#add_product_to_bundle_autocomplete', + 'add_product_to_bundle_button' => '#bitbag_add_product_to_bundle_button', + 'code' => '#sylius_product_code', + 'language_tab' => '[data-locale="%locale%"] .title', + 'name' => '#sylius_product_translations_%locale%_name', + 'original_price' => '#sylius_product_variant_channelPricings_%channelCode%_originalPrice', + 'price' => '#sylius_product_variant_channelPricings_%channelCode%_price', + 'slug' => '#sylius_product_translations_%locale%_slug', + 'tab' => '.menu [data-tab="%name%"]', + ]); + } + + private function clickTabIfItsNotActive(string $tabName): void + { + $attributesTab = $this->getElement('tab', ['%name%' => $tabName]); + if (!$attributesTab->hasClass('active')) { + $attributesTab->click(); + } + } + + private function getLastProductAutocomplete(): NodeElement + { + $items = $this->getDocument()->findAll('css', '#add_product_to_bundle_autocomplete'); + + Assert::notEmpty($items); + + return end($items); + } +} diff --git a/tests/Behat/Page/Admin/CreateBundledProductPageInterface.php b/tests/Behat/Page/Admin/CreateBundledProductPageInterface.php new file mode 100644 index 00000000..d7c4250a --- /dev/null +++ b/tests/Behat/Page/Admin/CreateBundledProductPageInterface.php @@ -0,0 +1,23 @@ + { Encore.reset();