diff --git a/behat.yml.dist b/behat.yml.dist index 01c63a7..72ba728 100644 --- a/behat.yml.dist +++ b/behat.yml.dist @@ -7,7 +7,7 @@ default: FriendsOfBehat\ContextServiceExtension: imports: - vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml - - tests/Behat/Resources/services.xml + - tests/Behat/Resources/services.yml FriendsOfBehat\SymfonyExtension: kernel: diff --git a/composer.json b/composer.json index 118595f..39802d4 100644 --- a/composer.json +++ b/composer.json @@ -32,13 +32,14 @@ "phpunit/phpunit": "^6.5", "se/selenium-server-standalone": "^2.52", "sylius-labs/coding-standard": "^2.0", - "symplify/easy-coding-standard": "^4.6" + "symplify/easy-coding-standard": "^4.6", + "behat/symfony2-extension": "^2.1" }, "prefer-stable": true, "autoload": { "psr-4": { "Locastic\\SyliusStoreLocatorPlugin\\": "src/", - "Tests\\Acme\\SyliusExamplePlugin\\": "tests/" + "Tests\\Locastic\\SyliusStoreLocatorPlugin\\": "tests/" } }, "autoload-dev": { diff --git a/features/checkout/shipping_method/seeing_store_locator_index_page.feature b/features/checkout/shipping_method/seeing_store_locator_index_page.feature new file mode 100644 index 0000000..ed2230c --- /dev/null +++ b/features/checkout/shipping_method/seeing_store_locator_index_page.feature @@ -0,0 +1,16 @@ +@store +Feature: Seeing store locator index page + As a Customer + I want to be able to see index page with all store locations listed + + Background: + Given the store operates on a single channel in "United States" + Given the store has "Dragon Store" store location + Given the store has "Bunny Store" store location + + @ui + Scenario: Seeing the store locator page + Given I am at the store locator index page + Then I should see the store map + And I should see the "Dragon Store" store location + And I should see the "Bunny Store" store location \ No newline at end of file diff --git a/features/checkout/shipping_method/seeing_store_locator_show_page.feature b/features/checkout/shipping_method/seeing_store_locator_show_page.feature new file mode 100644 index 0000000..4cbb3da --- /dev/null +++ b/features/checkout/shipping_method/seeing_store_locator_show_page.feature @@ -0,0 +1,31 @@ +@store +Feature: Seeing store locator show page + As a Customer + I want to be able to see store location page + + Background: + Given the store operates on a single channel in "United States" + Given the store has "Dragon Store" store location + Given the store has "Bunny Store" store location + Given the store has "Basic Bunny Store" store location with only required info + + @ui + Scenario: Seeing the single store locator page + Given I am at the store locator index page + And I should see the "Dragon Store" store location + And I should see the "Bunny Store" store location + And I should see the "Basic Bunny Store" store location + And I go to "Bunny Store" store location page + Then I should see the store map + And I should see "Bunny Store" store information + And I should see store images + + @ui + Scenario: Seeing the single store locator page with only required info + Given I am at the store locator index page + And I should see the "Basic Bunny Store" store location + And I go to "Basic Bunny Store" store location page + Then I should see the store map + And I should see "Basic Bunny Store" store title + And I should not see "Basic Bunny Store" store information + And I should not see store images \ No newline at end of file diff --git a/features/checkout/shipping_method/selecting_pick_up_at_store_shipping_method.feature b/features/checkout/shipping_method/selecting_pick_up_at_store_shipping_method.feature new file mode 100644 index 0000000..6198bec --- /dev/null +++ b/features/checkout/shipping_method/selecting_pick_up_at_store_shipping_method.feature @@ -0,0 +1,48 @@ +@store +Feature: Selecting shipping method with pickup at store + In order to pick up order at store + As a Customer + I want to be able to choose a shipping method with pickup at store option + + Background: + Given the store operates on a single channel in "United States" + Given the store has a product "Targaryen T-Shirt" priced at "$19.99" + Given the store has "Dragon Store" store location + Given the store has "other shipping method" shipping method with "$10.00" fee + Given the store has "pickup at store" shipping method with enabled store pickup and "$10.00" fee + + @ui + Scenario: Selecting store pickup shipping method + Given I have product "Targaryen T-Shirt" in the cart + And I am at the checkout addressing step + When I specify the email as "jon.snow@example.com" + And I specify the shipping address as "Ankh Morpork", "Frost Alley", "90210", "United States" for "Jon Snow" + And I complete the addressing step + Then I should be redirected to the shipping step + And I select "pickup at store" shipping method + Then I should see "Dragon Store" store location + When I select "Dragon Store" store location + And I complete the shipping step + + @ui + Scenario: Selecting store pickup shipping method without store selection + Given I have product "Targaryen T-Shirt" in the cart + And I am at the checkout addressing step + When I specify the email as "jon.snow@example.com" + And I specify the shipping address as "Ankh Morpork", "Frost Alley", "90210", "United States" for "Jon Snow" + And I complete the addressing step + Then I should be redirected to the shipping step + And I select "pickup at store" shipping method + And I complete the shipping step + Then I should see a validation error + + @ui + Scenario: I should be able to select other shipping method and not choose store + Given I have product "Targaryen T-Shirt" in the cart + And I am at the checkout addressing step + When I specify the email as "jon.snow@example.com" + And I specify the shipping address as "Ankh Morpork", "Frost Alley", "90210", "United States" for "Jon Snow" + And I complete the addressing step + Then I should be redirected to the shipping step + When I select "other shipping method" shipping method + And I complete the shipping step \ No newline at end of file diff --git a/phpspec.yml.dist b/phpspec.yml.dist index ae371ee..23daa01 100644 --- a/phpspec.yml.dist +++ b/phpspec.yml.dist @@ -1,4 +1,4 @@ suites: main: - namespace: Acme\SyliusExamplePlugin - psr4_prefix: Acme\SyliusExamplePlugin + namespace: Locastic\SyliusStoreLocatorPlugin + psr4_prefix: Locastic\SyliusStoreLocatorPlugin diff --git a/src/Entity/IsPickupAtStoreInterface.php b/src/Entity/IsPickupAtStoreInterface.php index c77b1fb..0ad4264 100644 --- a/src/Entity/IsPickupAtStoreInterface.php +++ b/src/Entity/IsPickupAtStoreInterface.php @@ -6,5 +6,5 @@ interface IsPickupAtStoreInterface { public function isPickupAtStore(): ?bool; - public function setPickupAtStore(bool $pickupAtLocation): void; + public function setPickupAtStore(bool $pickupAtStore): void; } diff --git a/src/Fixture/StoreFactory.php b/src/Fixture/StoreFactory.php new file mode 100644 index 0000000..8a0997e --- /dev/null +++ b/src/Fixture/StoreFactory.php @@ -0,0 +1,81 @@ +imageUploader = $imageUploader; + } + + /** + * {@inheritdoc} + */ + public function create(array $options = []) + { + $store = new Store(); + $store->setCode($options['code']); + + foreach ($options['translations'] as $locale => $translation) { + $pageTranslation = new StoreTranslation(); + $pageTranslation->setLocale($locale); + + $pageTranslation->setName($translation['name']); + $pageTranslation->setSlug($translation['slug']); + $pageTranslation->setContent($translation['content']); + $pageTranslation->setOpeningHours($translation['opening_hours']); + $pageTranslation->setMetaTitle($translation['meta_title']); + $pageTranslation->setMetaDescription($translation['meta_description']); + $pageTranslation->setMetaKeywords($translation['meta_keywords']); + + $store->addTranslation($pageTranslation); + } + + $store->setLatitude($options['latitude']); + $store->setLongitude($options['longitude']); + $store->setAddress($options['address']); + $store->setContactEmail($options['contact_email']); + $store->setContactPhone($options['contact_phone']); + $store->setPickupAtStoreAvailable($options['pickup_at_store_available']); + + foreach ($options['images'] as $image) { + if (!array_key_exists('path', $image)) { + $imagePath = array_shift($image); + $imageType = array_pop($image); + } else { + $imagePath = $image['path']; + $imageType = $image['type'] ?? null; + } + + $uploadedImage = new UploadedFile($imagePath, basename($imagePath)); + + /** @var StoreImageInterface $storeImage */ + $storeImage = new StoreImage(); + + $storeImage->setPath($imagePath); + $storeImage->setFile($uploadedImage); + $storeImage->setType($imageType); + + $this->imageUploader->upload($storeImage); + + $store->addImage($storeImage); + } + + return $store; + } +} \ No newline at end of file diff --git a/src/Form/EventListener/ShipmentFormEventListener.php b/src/Form/EventListener/ShipmentFormEventListener.php new file mode 100644 index 0000000..5361e8d --- /dev/null +++ b/src/Form/EventListener/ShipmentFormEventListener.php @@ -0,0 +1,41 @@ +message = $message; + } + + public static function getSubscribedEvents() + { + return [ + FormEvents::POST_SUBMIT => 'onPostSubmit', + ]; + } + + + public function onPostSubmit(FormEvent $event) + { + $shipment = $event->getData(); + $form = $event->getForm(); + + if (!$shipment instanceof Shipment || !$form->isValid()) { + return; + } + + if ($shipment->getMethod()->isPickupAtStore() && $shipment->getStore() === null) { + $form->addError(new FormError($this->message)); + } + } +} \ No newline at end of file diff --git a/src/Form/Extension/Checkout/ShipmentTypeExtension.php b/src/Form/Extension/Checkout/ShipmentTypeExtension.php index 5436487..83e4d9c 100644 --- a/src/Form/Extension/Checkout/ShipmentTypeExtension.php +++ b/src/Form/Extension/Checkout/ShipmentTypeExtension.php @@ -5,6 +5,7 @@ namespace Locastic\SyliusStoreLocatorPlugin\Form\Extension\Checkout; use Locastic\SyliusStoreLocatorPlugin\Entity\Store; +use Locastic\SyliusStoreLocatorPlugin\Form\EventListener\ShipmentFormEventListener; use Sylius\Bundle\CoreBundle\Form\Type\Checkout\ShipmentType; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractTypeExtension; @@ -13,6 +14,13 @@ final class ShipmentTypeExtension extends AbstractTypeExtension { + private $translator; + + public function __construct($translator) + { + $this->translator = $translator; + } + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->add( @@ -25,15 +33,18 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ->setParameter('pickupAtStoreAvailable', true); }, 'mapped' => true, - 'class' => Store::class, 'required' => false, + 'class' => Store::class, 'choice_label' => 'name', ] ); + + $message = $this->translator->trans('locastic_sylius_store_locator_plugin.shipment_method.store_not_null',[],'validators'); + $builder->addEventSubscriber(new ShipmentFormEventListener($message)); } - public function getExtendedType(): string + public static function getExtendedTypes(): iterable { - return ShipmentType::class; + return [ShipmentType::class]; } } diff --git a/src/Form/Extension/ShippingMethodTypeExtension.php b/src/Form/Extension/ShippingMethodTypeExtension.php index 70d9b3b..d64e1fd 100644 --- a/src/Form/Extension/ShippingMethodTypeExtension.php +++ b/src/Form/Extension/ShippingMethodTypeExtension.php @@ -13,17 +13,17 @@ public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add( - 'pickupAtLocation', + 'pickupAtStore', CheckboxType::class, [ - 'label' => 'locastic_sylius_store_locator_plugin.ui.pickup_at_location', + 'label' => 'locastic_sylius_store_locator_plugin.ui.pickup_at_store', 'required' => false, ] ); } - public function getExtendedType() + public static function getExtendedTypes(): iterable { - return ShippingMethodType::class; + return [ShippingMethodType::class]; } } diff --git a/src/Resources/config/config_pickup_at_store.yml b/src/Resources/config/config_pickup_at_store.yml index 8799595..14ac74f 100644 --- a/src/Resources/config/config_pickup_at_store.yml +++ b/src/Resources/config/config_pickup_at_store.yml @@ -4,6 +4,8 @@ imports: services: locastic_sylius_store_locator_plugin.form.extension.type.shipment: class: Locastic\SyliusStoreLocatorPlugin\Form\Extension\Checkout\ShipmentTypeExtension + arguments: + - '@translator' tags: - { name: form.type_extension, extended_type: Sylius\Bundle\CoreBundle\Form\Type\Checkout\ShipmentType } diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 99fb9ae..5a919ca 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -40,6 +40,12 @@ services: tags: - { name: sylius_fixtures.fixture } + locastic_sylius_store_locator_plugin.example_factory.store: + class: Locastic\SyliusStoreLocatorPlugin\Fixture\StoreFactory + arguments: + - '@sylius.image_uploader' + public: true + # listeners locastic_sylius_store_locator_plugin.listener.images_upload: diff --git a/src/Resources/config/validation/Shipment.yml b/src/Resources/config/validation/Shipment.yml deleted file mode 100644 index d4d9617..0000000 --- a/src/Resources/config/validation/Shipment.yml +++ /dev/null @@ -1,4 +0,0 @@ -Locastic\SyliusStoreLocatorPlugin\Entity\Shipment: - constraints: - - Locastic\SyliusStoreLocatorPlugin\Validator\Constraints\IsStorePopulated: - groups: ['sylius'] \ No newline at end of file diff --git a/src/Resources/public/js/locations.js b/src/Resources/public/js/locations.js index c5d6b89..b83c8df 100644 --- a/src/Resources/public/js/locations.js +++ b/src/Resources/public/js/locations.js @@ -123,4 +123,11 @@ map.setAddress(); }); + //This is temporary fix for app.js addition of hidden class. + setTimeout(function () { + + $('#locastic_sylius_store_locator_plugin_store_enabled').removeClass('hidden'); + $('#locastic_sylius_store_locator_plugin_store_pickupAtStoreAvailable').removeClass('hidden'); + }, 500); + }(window.jQuery)); \ No newline at end of file diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml index 9fb9d08..ccfc662 100644 --- a/src/Resources/translations/messages.en.yml +++ b/src/Resources/translations/messages.en.yml @@ -20,5 +20,5 @@ locastic_sylius_store_locator_plugin: opening_hours: Opening hours see_more: See More pickup_at_store_available: Pickup At Store Available - pickup_at_location: Pickup At Location + pickup_at_store: Pickup At Location images: Images of store diff --git a/src/Resources/translations/messages.hr.yml b/src/Resources/translations/messages.hr.yml index f75bd3f..8838263 100644 --- a/src/Resources/translations/messages.hr.yml +++ b/src/Resources/translations/messages.hr.yml @@ -20,5 +20,5 @@ locastic_sylius_store_locator_plugin: opening_hours: Radno vrijeme see_more: Pogledaj više pickup_at_store_available: Dostupno preuzimanje u trgovini - pickup_at_location: Preuzimanje u trgovini + pickup_at_store: Preuzimanje u trgovini images: Slike trgovine diff --git a/src/Resources/views/Shop/Store/show.html.twig b/src/Resources/views/Shop/Store/show.html.twig index e1feb94..ee2f3d4 100644 --- a/src/Resources/views/Shop/Store/show.html.twig +++ b/src/Resources/views/Shop/Store/show.html.twig @@ -7,7 +7,7 @@ {% if store.metaTitle %} {{ store.metaTitle }} {% else %} - {{ store.title }} + {{ store.name }} {% endif %} {% endblock %} @@ -38,45 +38,51 @@
-
-

{{ 'locastic_sylius_store_locator_plugin.ui.address'|trans }} - : {{ store.address }}

-

{{ 'locastic_sylius_store_locator_plugin.ui.contact_email'|trans }} - : {{ store.contactEmail }}

-

- {{ 'locastic_sylius_store_locator_plugin.ui.contact_phone'|trans }} - : {{ store.contactPhone }}

-

- {{ 'locastic_sylius_store_locator_plugin.ui.opening_hours'|trans }} - : {{ store.openingHours|raw }}

- +
+ {% if store.address %} +

{{ 'locastic_sylius_store_locator_plugin.ui.address'|trans }} + : {{ store.address }}

+ {% endif %} + {% if store.contactEmail %} +

{{ 'locastic_sylius_store_locator_plugin.ui.contact_email'|trans }} + : {{ store.contactEmail }}

+ {% endif %} + {% if store.contactPhone %} +

+ {{ 'locastic_sylius_store_locator_plugin.ui.contact_phone'|trans }} + : {{ store.contactPhone }}

+ {% endif %} + {% if store.openingHours %} +

+ {{ 'locastic_sylius_store_locator_plugin.ui.opening_hours'|trans }} + : {{ store.openingHours|raw }}

+ {% endif %}
{{ store.content|raw }}
-
-
- {% if store.images %} -

- {{ 'locastic_sylius_store_locator_plugin.ui.images'|trans }} -

+ {% if store.images|length > 0 %} +

+ {{ 'locastic_sylius_store_locator_plugin.ui.images'|trans }} +

-
- {% for storeImage in store.images %} -
+
+ {% for storeImage in store.images %} +
- {% set imagePath = '/media/image/'~storeImage.path %} - + {% set imagePath = '/media/image/'~storeImage.path %} + -
-

{{ storeImage.type }}

+
+

{{ storeImage.type }}

+
-
- {% endfor %} -
+ {% endfor %} +
{% endif %}
{% endblock %} diff --git a/src/Resources/views/Store/Crud/_form.html.twig b/src/Resources/views/Store/Crud/_form.html.twig index e99dfe6..47ac3a0 100644 --- a/src/Resources/views/Store/Crud/_form.html.twig +++ b/src/Resources/views/Store/Crud/_form.html.twig @@ -49,7 +49,7 @@ {% block javascripts %} - {% include '@SyliusUi/_javascripts.html.twig' with {'path': 'assets/shop/js/app.js'} %} + {% include '@SyliusUi/_javascripts.html.twig' with {'path': 'assets/admin/js/app.js'} %} {% include '@SyliusUi/_javascripts.html.twig' with {'path': 'bundles/locasticsyliusstorelocatorplugin/js/locations.js'} %} {% include '@SyliusUi/_javascripts.html.twig' with {'path': 'bundles/locasticsyliusstorelocatorplugin/js/locationImages.js'} %} {% endblock %} \ No newline at end of file diff --git a/src/Validator/Constraints/IsStorePopulated.php b/src/Validator/Constraints/IsStorePopulated.php deleted file mode 100644 index 46dc285..0000000 --- a/src/Validator/Constraints/IsStorePopulated.php +++ /dev/null @@ -1,21 +0,0 @@ -getMethod()->isPickupAtStore() && $value->getStore() === null) { - $this->context - ->buildViolation($constraint->message) - ->atPath('method') - ->addViolation(); - } - } -} diff --git a/tests/Application/app/Resources/SyliusAdminBundle/views/ShippingMethod/_form.html.twig b/tests/Application/app/Resources/SyliusAdminBundle/views/ShippingMethod/_form.html.twig index 39ab5c9..7da13af 100644 --- a/tests/Application/app/Resources/SyliusAdminBundle/views/ShippingMethod/_form.html.twig +++ b/tests/Application/app/Resources/SyliusAdminBundle/views/ShippingMethod/_form.html.twig @@ -8,7 +8,7 @@ {{ form_row(form.code) }} {{ form_row(form.zone) }} {{ form_row(form.position) }} - {{ form_row(form.pickupAtLocation) }} + {{ form_row(form.pickupAtStore) }}
{{ form_row(form.enabled) }}

{{ 'sylius.ui.availability'|trans }}

diff --git a/tests/Behat/Context/Setup/ShippingContext.php b/tests/Behat/Context/Setup/ShippingContext.php new file mode 100644 index 0000000..06bfcd8 --- /dev/null +++ b/tests/Behat/Context/Setup/ShippingContext.php @@ -0,0 +1,113 @@ +sharedStorage = $sharedStorage; + $this->shippingMethodFactory = $shippingMethodExampleFactory; + $this->shippingMethodRepository = $shippingMethodRepository; + } + + /** + * @Given /^the store has "([^"]+)" shipping method with enabled store pickup and ("[^"]+") fee$/ + */ + public function storeHasShippingMethodWithFeeAndPickupAtStore($shippingMethodName, $fee) + { + $channel = $this->sharedStorage->get('channel'); + $configuration = $this->getConfigurationByChannels([$channel], $fee); + + $this->saveShippingMethod( + $this->shippingMethodFactory->create( + [ + 'name' => $shippingMethodName, + 'enabled' => true, + 'is_pickup_at_store' => true, + 'zone' => $this->getShippingZone(), + 'calculator' => [ + 'type' => DefaultCalculators::FLAT_RATE, + 'configuration' => $configuration, + ], + 'channels' => [$this->sharedStorage->get('channel')], + ] + ) + ); + } + + /** + * @return ZoneInterface + */ + private function getShippingZone() + { + if ($this->sharedStorage->has('shipping_zone')) { + return $this->sharedStorage->get('shipping_zone'); + } + + return $this->sharedStorage->get('zone'); + } + + /** + * @param ShippingMethodInterface $shippingMethod + */ + private function saveShippingMethod(ShippingMethodInterface $shippingMethod) + { + $this->shippingMethodRepository->add($shippingMethod); + $this->sharedStorage->set('shipping_method', $shippingMethod); + + } + + /** + * @param array $channels + * @param int $amount + * + * @return array + */ + private function getConfigurationByChannels(array $channels, $amount = 0) + { + $configuration = []; + + /** @var ChannelInterface $channel */ + foreach ($channels as $channel) { + $configuration[$channel->getCode()] = ['amount' => $amount]; + } + + return $configuration; + } + +} \ No newline at end of file diff --git a/tests/Behat/Context/Setup/StoreContext.php b/tests/Behat/Context/Setup/StoreContext.php new file mode 100644 index 0000000..cacef8d --- /dev/null +++ b/tests/Behat/Context/Setup/StoreContext.php @@ -0,0 +1,123 @@ +storeRepository = $storeRepository; + $this->storeFactory = $storeFactory; + $this->sharedStorage = $sharedStorage; + } + + /** + * @Given /^the store has "([^"]*)" store location$/ + */ + public function theStoreHasStoreLocation($storeName) + { + $this->saveStore( + $this->storeFactory->create( + [ + 'latitude' => 43.5129188, + 'longitude' => 16.48521519999997, + 'address' => "Lovački put 7, 21000, Split, Croatia", + 'contact_phone' => '+38521782059', + 'contact_email' => 'info@locastic.com', + 'pickup_at_store_available' => true, + 'code' => StringInflector::nameToCode(strtolower($storeName)), + 'translations' => [ + 'en_US' => [ + 'name' => $storeName, + 'slug' => StringInflector::nameToCode(strtolower($storeName)), + 'meta_title' => $storeName, + 'meta_description' => $storeName, + 'meta_keywords' => $storeName, + 'content' => $storeName, + 'opening_hours' => 'Mon-Fri 09-17h', + ], + ], + 'images' => [ + 'dragovode_1' => + [ + 'type' => "Office inside", + 'path' => __DIR__."/../../../Application/app/Resources/fixtures/office_1.jpg", + ], + ], + ] + ) + ); + } + + /** + * @Given /^the store has "([^"]*)" store location with only required info$/ + */ + public function theStoreHasStoreLocationWithOnlyRequiredInfo($storeName) + { + $this->saveStore( + $this->storeFactory->create( + [ + 'latitude' => null, + 'longitude' => null, + 'address' => null, + 'contact_phone' => null, + 'contact_email' => null, + 'pickup_at_store_available' => true, + 'code' => StringInflector::nameToCode(strtolower($storeName)), + 'translations' => [ + 'en_US' => [ + 'name' => $storeName, + 'slug' => StringInflector::nameToCode(strtolower($storeName)), + 'meta_title' => null, + 'meta_description' => null, + 'meta_keywords' => null, + 'content' => null, + 'opening_hours' => null, + ], + ], + 'images' => [], + ] + ) + ); + } + + /** + * @param StoreInterface $store + */ + private function saveStore(StoreInterface $store) + { + $this->storeRepository->add($store); + $this->sharedStorage->set('store', $store); + } +} \ No newline at end of file diff --git a/tests/Behat/Context/Ui/Shop/Checkout/CheckoutShippingContext.php b/tests/Behat/Context/Ui/Shop/Checkout/CheckoutShippingContext.php new file mode 100644 index 0000000..4554d71 --- /dev/null +++ b/tests/Behat/Context/Ui/Shop/Checkout/CheckoutShippingContext.php @@ -0,0 +1,240 @@ +selectShippingPage = $selectShippingPage; + $this->selectPaymentPage = $selectPaymentPage; + $this->completePage = $completePage; + } + + /** + * @Then /^I should see "([^"]*)" store location$/ + */ + public function iShouldSeeStoreLocation($storeName) + { + Assert::true($this->selectShippingPage->hasStoreSelector($storeName)); + } + + /** + * @Then /^I should see a validation error$/ + */ + public function iShouldSeeValidationError() + { + Assert::notNull($this->selectShippingPage->hasValidationError()); + } + + /** + * @When /^I select "([^"]*)" store location$/ + */ + public function iSelectStoreLocation($storeName) + { + $this->selectShippingPage->selectStore($storeName); + } + + /** + * @Then /^I should see selected "([^"]*)" store location$/ + */ + public function iShouldSeeSelectedStoreLocation($storeName) + { + Assert::same($this->selectShippingPage->getSelectedStoreName(), $storeName); + } + + + /** + * @Given I have proceeded selecting :shippingMethodName shipping method + * @When I proceed with :shippingMethodName shipping method + */ + public function iHaveProceededSelectingShippingMethod($shippingMethodName) + { + $this->iSelectShippingMethod($shippingMethodName); + $this->selectShippingPage->nextStep(); + } + + /** + * @Given I have selected :shippingMethod shipping method + * @When I select :shippingMethod shipping method + */ + public function iSelectShippingMethod($shippingMethod) + { + $this->selectShippingPage->selectShippingMethod($shippingMethod); + } + + /** + * @When I try to open checkout shipping page + */ + public function iTryToOpenCheckoutShippingPage() + { + $this->selectShippingPage->tryToOpen(); + } + + /** + * @When /^I(?:| try to) complete the shipping step$/ + */ + public function iCompleteTheShippingStep() + { + $this->selectShippingPage->nextStep(); + } + + /** + * @When I decide to change my address + */ + public function iDecideToChangeMyAddress() + { + $this->selectShippingPage->changeAddress(); + } + + /** + * @When I go back to shipping step of the checkout + */ + public function iGoBackToShippingStepOfTheCheckout() + { + $this->selectShippingPage->open(); + } + + /** + * @Then I should not be able to select :shippingMethodName shipping method + */ + public function iShouldNotBeAbleToSelectShippingMethod($shippingMethodName) + { + Assert::false(in_array($shippingMethodName, $this->selectShippingPage->getShippingMethods(), true)); + } + + /** + * @Then I should have :shippingMethodName shipping method available as the first choice + */ + public function iShouldHaveShippingMethodAvailableAsFirstChoice($shippingMethodName) + { + $shippingMethods = $this->selectShippingPage->getShippingMethods(); + + Assert::same(reset($shippingMethods), $shippingMethodName); + } + + /** + * @Then I should have :shippingMethodName shipping method available as the last choice + */ + public function iShouldHaveShippingMethodAvailableAsLastChoice($shippingMethodName) + { + $shippingMethods = $this->selectShippingPage->getShippingMethods(); + + Assert::same(end($shippingMethods), $shippingMethodName); + } + + /** + * @Then I should be on the checkout shipping step + * @Then I should be redirected to the shipping step + */ + public function iShouldBeOnTheCheckoutShippingStep() + { + $this->selectShippingPage->open(); + $this->selectShippingPage->verify(); + } + + /** + * @Then I should be informed that my order cannot be shipped to this address + */ + public function iShouldBeInformedThatMyOrderCannotBeShippedToThisAddress() + { + Assert::true($this->selectShippingPage->hasNoShippingMethodsMessage()); + } + + /** + * @Then I should be able to go to the complete step again + */ + public function iShouldBeAbleToGoToTheCompleteStepAgain() + { + $this->selectShippingPage->nextStep(); + + $this->completePage->verify(); + } + + /** + * @Then I should be able to go to the payment step again + */ + public function iShouldBeAbleToGoToThePaymentStepAgain() + { + $this->selectShippingPage->nextStep(); + + $this->selectPaymentPage->verify(); + } + + /** + * @Then I should see shipping method :shippingMethodName with fee :fee + */ + public function iShouldSeeShippingFee($shippingMethodName, $fee) + { + Assert::true($this->selectShippingPage->hasShippingMethodFee($shippingMethodName, $fee)); + } + + /** + * @Then there should be information about no available shipping methods + */ + public function thereShouldBeInformationAboutNoShippingMethodsAvailableForMyShippingAddress() + { + Assert::true($this->selectShippingPage->hasNoAvailableShippingMethodsWarning()); + } + + /** + * @Then I should see :shippingMethodName shipping method + */ + public function iShouldSeeShippingMethod($shippingMethodName) + { + Assert::true($this->selectShippingPage->hasShippingMethod($shippingMethodName)); + } + + /** + * @Then I should see selected :shippingMethodName shipping method + */ + public function iShouldSeeSelectedShippingMethod($shippingMethodName) + { + Assert::same($this->selectShippingPage->getSelectedShippingMethodName(), $shippingMethodName); + } + + /** + * @Then I should not see :shippingMethodName shipping method + */ + public function iShouldNotSeeShippingMethod($shippingMethodName) + { + Assert::false($this->selectShippingPage->hasShippingMethod($shippingMethodName)); + } + + /** + * @Then I should be checking out as :email + */ + public function iShouldBeCheckingOutAs($email) + { + Assert::same($this->selectShippingPage->getPurchaserEmail(), 'Checking out as ' . $email . '.'); + } +} \ No newline at end of file diff --git a/tests/Behat/Context/Ui/Shop/Store/StoreContext.php b/tests/Behat/Context/Ui/Shop/Store/StoreContext.php new file mode 100644 index 0000000..7466994 --- /dev/null +++ b/tests/Behat/Context/Ui/Shop/Store/StoreContext.php @@ -0,0 +1,107 @@ +storeIndexPage = $storeIndexPage; + $this->storeShowPage = $storeShowPage; + } + + + /** + * @Given I am at the store locator index page + * @When I go back to store locator index page + */ + public function iAmAtTheStoreLocatorIndexPage() + { + $this->storeIndexPage->open(); + } + + /** + * @Then /^I should see the store map$/ + */ + public function iShouldSeeTheStoreMap() + { + Assert::notNull($this->storeIndexPage->hasStoreMap()); + } + + /** + * @Given /^I should see the "([^"]*)" store location$/ + */ + public function iShouldSeeTheStoreLocation($storeName) + { + Assert::true($this->storeIndexPage->hasStorePreview($storeName)); + } + + /** + * @Given /^I go to "([^"]*)" store location page$/ + */ + public function iGoToStoreLocationPage($storeName) + { + $this->storeShowPage->open(['slug' => StringInflector::nameToCode(strtolower($storeName))]); + } + + /** + * @Given /^I should see "([^"]*)" store information$/ + */ + public function iShouldSeeStoreInformation($storeName) + { + Assert::true($this->storeShowPage->hasStoreInformation($storeName)); + } + + /** + * @Given /^I should see store images$/ + */ + public function iShouldSeeStoreImages() + { + Assert::true($this->storeShowPage->hasStoreImage()); + } + + /** + * @Given /^I should not see "([^"]*)" store information$/ + */ + public function iShouldNotSeeStoreInformation($storeName) + { + Assert::true($this->storeShowPage->doesntHaveStoreInformation()); + } + + /** + * @Given /^I should see "([^"]*)" store title$/ + */ + public function iShouldSeeStoreTitle($storeName) + { + Assert::true($this->storeShowPage->hasStoreTitle($storeName)); + } + + /** + * @Given /^I should not see store images$/ + */ + public function iShouldNotSeeStoreImages() + { + Assert::false($this->storeShowPage->hasStoreImage()); + } +} \ No newline at end of file diff --git a/tests/Behat/Context/Ui/Shop/WelcomeContext.php b/tests/Behat/Context/Ui/Shop/WelcomeContext.php deleted file mode 100644 index a0d95b0..0000000 --- a/tests/Behat/Context/Ui/Shop/WelcomeContext.php +++ /dev/null @@ -1,80 +0,0 @@ -staticWelcomePage = $staticWelcomePage; - $this->dynamicWelcomePage = $dynamicWelcomePage; - } - - /** - * @When a customer with an unknown name visits static welcome page - */ - public function customerWithUnknownNameVisitsStaticWelcomePage(): void - { - $this->staticWelcomePage->open(); - } - - /** - * @When a customer named :name visits static welcome page - */ - public function namedCustomerVisitsStaticWelcomePage(string $name): void - { - $this->staticWelcomePage->open(['name' => $name]); - } - - /** - * @Then they should be statically greeted with :greeting - */ - public function theyShouldBeStaticallyGreetedWithGreeting(string $greeting): void - { - Assert::same($this->staticWelcomePage->getGreeting(), $greeting); - } - - /** - * @When a customer with an unknown name visits dynamic welcome page - */ - public function customerWithUnknownNameVisitsDynamicWelcomePage(): void - { - $this->dynamicWelcomePage->open(); - } - - /** - * @When a customer named :name visits dynamic welcome page - */ - public function namedCustomerVisitsDynamicWelcomePage(string $name): void - { - $this->dynamicWelcomePage->open(['name' => $name]); - } - - /** - * @Then they should be dynamically greeted with :greeting - */ - public function theyShouldBeDynamicallyGreetedWithGreeting(string $greeting): void - { - Assert::same($this->dynamicWelcomePage->getGreeting(), $greeting); - } -} diff --git a/tests/Behat/Page/Shop/Checkout/SelectShippingPage.php b/tests/Behat/Page/Shop/Checkout/SelectShippingPage.php new file mode 100644 index 0000000..2df4018 --- /dev/null +++ b/tests/Behat/Page/Shop/Checkout/SelectShippingPage.php @@ -0,0 +1,73 @@ +getSession()->getPage()->find('css', '.sylius-validation-error'); + } + + /** + * {@inheritdoc} + */ + public function hasStoreSelector($storeName) + { + $inputs = $this->getSession()->getPage()->findAll('css', '.store-select .dropdown option'); + + $stores = []; + foreach ($inputs as $input) { + $stores[] = trim($input->getText()); + } + + return in_array($storeName, $stores); + } + + /** + * {@inheritdoc} + */ + public function selectStore($storeName) + { + $storeOptionElement = $this->getElement('store_option', ['%store%' => $storeName]); + $storeOptionValue = $storeOptionElement->getAttribute('value'); + + $this->getElement('store_select')->selectOption($storeOptionValue); + } + + /** + * {@inheritdoc} + */ + public function getSelectedStoreName() + { + $storeNames = $this->getSession()->getPage()->findAll('css', '.store-select .dropdown option'); + + /** @var NodeElement $storeName */ + foreach ($storeNames as $storeName) { + if (null !== $storeName->find('css', ':selected')) { + return $storeName->getText(); + } + } + + return null; + } + + + /** + * {@inheritdoc} + */ + protected function getDefinedElements() + { + return array_merge( + parent::getDefinedElements(), + [ + 'store_select_outer' => '.store-select', + 'store_select' => '.store-select .dropdown', + 'store_option' => '.store-select .dropdown option:contains("%store%")', + ] + ); + } +} \ No newline at end of file diff --git a/tests/Behat/Page/Shop/Checkout/SelectShippingPageInterface.php b/tests/Behat/Page/Shop/Checkout/SelectShippingPageInterface.php new file mode 100644 index 0000000..53ffa95 --- /dev/null +++ b/tests/Behat/Page/Shop/Checkout/SelectShippingPageInterface.php @@ -0,0 +1,16 @@ +getSession()->getPage()->waitFor(3, function (): string { - $greeting = $this->getElement('greeting')->getText(); - - if ('Loading...' === $greeting) { - return ''; - } - - return $greeting; - }); - } - - /** - * {@inheritdoc} - */ - public function getRouteName(): string - { - return 'acme_sylius_example_dynamic_welcome'; - } - - /** - * {@inheritdoc} - */ - protected function getDefinedElements(): array - { - return array_merge(parent::getDefinedElements(), [ - 'greeting' => '#greeting', - ]); - } -} diff --git a/tests/Behat/Page/Shop/StaticWelcomePage.php b/tests/Behat/Page/Shop/StaticWelcomePage.php deleted file mode 100644 index 3b663a6..0000000 --- a/tests/Behat/Page/Shop/StaticWelcomePage.php +++ /dev/null @@ -1,36 +0,0 @@ -getElement('greeting')->getText(); - } - - /** - * {@inheritdoc} - */ - public function getRouteName(): string - { - return 'acme_sylius_example_static_welcome'; - } - - /** - * {@inheritdoc} - */ - protected function getDefinedElements(): array - { - return array_merge(parent::getDefinedElements(), [ - 'greeting' => '#greeting', - ]); - } -} diff --git a/tests/Behat/Page/Shop/Store/StoreIndexPage.php b/tests/Behat/Page/Shop/Store/StoreIndexPage.php new file mode 100644 index 0000000..0425cb9 --- /dev/null +++ b/tests/Behat/Page/Shop/Store/StoreIndexPage.php @@ -0,0 +1,41 @@ +getSession()->getPage()->find('css', '#map'); + + } + + public function hasStorePreview($storeName) + { + $stores = $this->getSession()->getPage()->findAll('css', '.store-location'); + /** @var NodeElement $store */ + + foreach ($stores as $store) { + if (null !== $store->find('css', 'h3 a')) { + $storeTitle = $store->find('css', 'h3 a'); + if($storeTitle->getText() === $storeName){ + return true; + } + } + } + + return false; + } +} \ No newline at end of file diff --git a/tests/Behat/Page/Shop/Store/StoreIndexPageInterface.php b/tests/Behat/Page/Shop/Store/StoreIndexPageInterface.php new file mode 100644 index 0000000..cd68600 --- /dev/null +++ b/tests/Behat/Page/Shop/Store/StoreIndexPageInterface.php @@ -0,0 +1,10 @@ +getSession()->getPage()->find('css', 'h1.header'); + + if ($titleElement->getText() !== $storeName) { + $hasStoreInfo = false; + } + + $infoElements = $this->getSession()->getPage()->findAll('css', '#store-location p'); + + $requiredItemProps = ["address", "email", "telephone", "hours"]; + $existingItemProps = []; + foreach ($infoElements as $infoElement) { + $existingItemProps[] = $infoElement->getAttribute('itemprop'); + } + + $missingItemProps = array_diff($requiredItemProps, $existingItemProps); + if (!empty($missingItemProps)) { + $hasStoreInfo = false; + } + + $descriptionElements = $this->getSession()->getPage()->findAll('css', '#store-location div'); + $existingItemProps = []; + foreach ($descriptionElements as $descriptionElement) { + $existingItemProps[] = $descriptionElement->getAttribute('itemprop'); + } + + if (!in_array('description', $existingItemProps)) { + $hasStoreInfo = false; + } + + return $hasStoreInfo; + + } + + public function hasStoreTitle($storeName) + { + $titleElement = $this->getSession()->getPage()->find('css', 'h1.header'); + + return !($titleElement->getText() !== $storeName); + } + + public function doesntHaveStoreInformation() + { + $hasStoreInfo = false; + + $infoElements = $this->getSession()->getPage()->findAll('css', '#store-location p'); + + $existingItemProps = []; + foreach ($infoElements as $infoElement) { + $existingItemProps[] = $infoElement->getAttribute('itemprop'); + } + + if (!empty($existingItemProps)) { + $hasStoreInfo = true; + } + + $descriptionElements = $this->getSession()->getPage()->findAll('css', '#store-location div'); + $existingItemProps = []; + foreach ($descriptionElements as $descriptionElement) { + $existingItemProps[] = $descriptionElement->getAttribute('itemprop'); + } + + if (in_array('description', $existingItemProps)) { + $hasStoreInfo = true; + } + + return $hasStoreInfo; + + } + + public function hasStoreImage() + { + $imgElement = $this->getSession()->getPage()->find('css', 'img.dimmable'); + + return $imgElement !== null; + } +} \ No newline at end of file diff --git a/tests/Behat/Page/Shop/Store/StoreShowPageInterface.php b/tests/Behat/Page/Shop/Store/StoreShowPageInterface.php new file mode 100644 index 0000000..38fe26d --- /dev/null +++ b/tests/Behat/Page/Shop/Store/StoreShowPageInterface.php @@ -0,0 +1,10 @@ + - - - - - - - - - - - - - - - diff --git a/tests/Behat/Resources/services.yml b/tests/Behat/Resources/services.yml new file mode 100644 index 0000000..e829632 --- /dev/null +++ b/tests/Behat/Resources/services.yml @@ -0,0 +1,57 @@ +services: + #shop + + locastic_sylius_store_locator.context.ui.shop.checkout.shipping: + class: Tests\Locastic\SyliusStoreLocatorPlugin\Behat\Context\Ui\Shop\Checkout\CheckoutShippingContext + arguments: + - "@locastic_sylius_store_locator.page.shop.select_shipping" + - "@sylius.behat.page.shop.checkout.select_payment" + - "@sylius.behat.page.shop.checkout.complete" + tags: + - fob.context_service + + locastic_sylius_store_locator.context.ui.shop.store: + class: Tests\Locastic\SyliusStoreLocatorPlugin\Behat\Context\Ui\Shop\Store\StoreContext + arguments: + - "@locastic_sylius_store_locator.page.shop.store_index" + - "@locastic_sylius_store_locator.page.shop.store_show" + tags: + - fob.context_service + + + #setup + + locastic_sylius_store_locator.behat.context.setup.shipping: + class: Tests\Locastic\SyliusStoreLocatorPlugin\Behat\Context\Setup\ShippingContext + arguments: + - "@sylius.behat.shared_storage" + - "@__symfony__.app.fixture.example_factory.shipping_method" + - "@__symfony__.sylius.repository.shipping_method" + tags: + - fob.context_service + + locastic_sylius_store_locator.behat.context.setup.store: + class: Tests\Locastic\SyliusStoreLocatorPlugin\Behat\Context\Setup\StoreContext + arguments: + - "@__symfony__.locastic_sylius_store_locator_plugin.repository.store" + - "@__symfony__.locastic_sylius_store_locator_plugin.example_factory.store" + - "@sylius.behat.shared_storage" + tags: + - fob.context_service + + #pages + + locastic_sylius_store_locator.page.shop.select_shipping: + class: Tests\Locastic\SyliusStoreLocatorPlugin\Behat\Page\Shop\Checkout\SelectShippingPage + public: false + parent: sylius.behat.page.shop.checkout.select_shipping + + locastic_sylius_store_locator.page.shop.store_index: + class: Tests\Locastic\SyliusStoreLocatorPlugin\Behat\Page\Shop\Store\StoreIndexPage + public: false + parent: sylius.behat.symfony_page + + locastic_sylius_store_locator.page.shop.store_show: + class: Tests\Locastic\SyliusStoreLocatorPlugin\Behat\Page\Shop\Store\StoreShowPage + public: false + parent: sylius.behat.symfony_page diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index 914463a..74f8434 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -1,10 +1,64 @@ -# Put your Behat suites definitions here - default: suites: - greeting_customer: + store_pickup: contexts_services: - - acme_sylius_example.context.ui.shop.welcome + - locastic_sylius_store_locator.behat.context.setup.shipping + - locastic_sylius_store_locator.behat.context.setup.store + + - locastic_sylius_store_locator.context.ui.shop.checkout.shipping + - locastic_sylius_store_locator.context.ui.shop.store + + - sylius.behat.context.hook.doctrine_orm + - sylius.behat.context.hook.email_spool + + - sylius.behat.context.transform.address + - sylius.behat.context.transform.country + - sylius.behat.context.transform.channel + - sylius.behat.context.transform.currency + - sylius.behat.context.transform.lexical + - sylius.behat.context.transform.locale + - sylius.behat.context.transform.order + - sylius.behat.context.transform.payment + - sylius.behat.context.transform.product + - sylius.behat.context.transform.product_option + - sylius.behat.context.transform.province + - sylius.behat.context.transform.shared_storage + - sylius.behat.context.transform.shipping_category + - sylius.behat.context.transform.shipping_method + - sylius.behat.context.transform.tax_category + - sylius.behat.context.transform.user + - sylius.behat.context.transform.zone + + - sylius.behat.context.setup.address + - sylius.behat.context.setup.admin_user + - sylius.behat.context.setup.channel + - sylius.behat.context.setup.currency + - sylius.behat.context.setup.customer + - sylius.behat.context.setup.exchange_rate + - sylius.behat.context.setup.geographical + - sylius.behat.context.setup.locale + - sylius.behat.context.setup.payment + - sylius.behat.context.setup.product + - sylius.behat.context.setup.promotion + - sylius.behat.context.setup.shop_security + - sylius.behat.context.setup.shipping + - sylius.behat.context.setup.shipping_category + - sylius.behat.context.setup.taxation + - sylius.behat.context.setup.user + - sylius.behat.context.setup.zone + - sylius.behat.context.ui.admin.managing_orders + - sylius.behat.context.ui.channel + - sylius.behat.context.ui.email + - sylius.behat.context.ui.shop.address_book + - sylius.behat.context.ui.shop.cart + - sylius.behat.context.ui.shop.checkout + - sylius.behat.context.ui.shop.checkout.addressing + - sylius.behat.context.ui.shop.checkout.complete + - sylius.behat.context.ui.shop.checkout.payment + - sylius.behat.context.ui.shop.checkout.thank_you + - sylius.behat.context.ui.shop.currency + - sylius.behat.context.ui.shop.homepage + - sylius.behat.context.ui.shop.locale filters: - tags: "@greeting_customer" + tags: "@store && @ui"